ArnoChen
commited on
Commit
·
0e5f52f
1
Parent(s):
07feee7
add document manager and site heaer
Browse files- lightrag_webui/bun.lock +61 -10
- lightrag_webui/eslint.config.js +27 -23
- lightrag_webui/package.json +12 -5
- lightrag_webui/src/App.tsx +21 -3
- lightrag_webui/src/api/lightrag.ts +89 -138
- lightrag_webui/src/components/MessageAlert.tsx +5 -4
- lightrag_webui/src/components/PropertiesView.tsx +1 -1
- lightrag_webui/src/components/document/ClearDocumentsDialog.tsx +52 -0
- lightrag_webui/src/components/document/UploadDocumentsDialog.tsx +91 -0
- lightrag_webui/src/components/ui/AsyncSearch.tsx +1 -1
- lightrag_webui/src/components/ui/Badge.tsx +33 -0
- lightrag_webui/src/components/ui/Card.tsx +55 -0
- lightrag_webui/src/components/ui/DataTable.tsx +64 -0
- lightrag_webui/src/components/ui/Dialog.tsx +3 -3
- lightrag_webui/src/components/ui/EmptyCard.tsx +38 -0
- lightrag_webui/src/components/ui/FileUploader.tsx +322 -0
- lightrag_webui/src/components/ui/Progress.tsx +23 -0
- lightrag_webui/src/components/ui/ScrollArea.tsx +44 -0
- lightrag_webui/src/components/ui/Table.tsx +94 -0
- lightrag_webui/src/components/ui/Tabs.tsx +53 -0
- lightrag_webui/src/components/ui/Tooltip.tsx +1 -1
- lightrag_webui/src/features/DocumentManager.tsx +166 -0
- lightrag_webui/src/{GraphViewer.tsx → features/GraphViewer.tsx} +2 -2
- lightrag_webui/src/features/SiteHeader.tsx +51 -0
- lightrag_webui/src/hooks/useLightragGraph.tsx +10 -2
- lightrag_webui/src/index.css +25 -0
- lightrag_webui/src/lib/constants.ts +15 -0
- lightrag_webui/src/stores/graph.ts +1 -1
lightrag_webui/bun.lock
CHANGED
@@ -4,13 +4,17 @@
|
|
4 |
"": {
|
5 |
"name": "lightrag-webui",
|
6 |
"dependencies": {
|
7 |
-
"@faker-js/faker": "^9.
|
8 |
"@radix-ui/react-checkbox": "^1.1.4",
|
9 |
"@radix-ui/react-dialog": "^1.1.6",
|
10 |
"@radix-ui/react-popover": "^1.1.6",
|
|
|
|
|
11 |
"@radix-ui/react-separator": "^1.1.2",
|
12 |
"@radix-ui/react-slot": "^1.1.2",
|
|
|
13 |
"@radix-ui/react-tooltip": "^1.1.8",
|
|
|
14 |
"@react-sigma/core": "^5.0.2",
|
15 |
"@react-sigma/graph-search": "^5.0.3",
|
16 |
"@react-sigma/layout-circlepack": "^5.0.2",
|
@@ -22,6 +26,7 @@
|
|
22 |
"@react-sigma/minimap": "^5.0.2",
|
23 |
"@sigma/edge-curve": "^3.1.0",
|
24 |
"@sigma/node-border": "^3.0.0",
|
|
|
25 |
"class-variance-authority": "^0.7.1",
|
26 |
"clsx": "^2.1.1",
|
27 |
"cmdk": "^1.0.4",
|
@@ -31,8 +36,10 @@
|
|
31 |
"minisearch": "^7.1.1",
|
32 |
"react": "^19.0.0",
|
33 |
"react-dom": "^19.0.0",
|
|
|
34 |
"seedrandom": "^3.0.5",
|
35 |
"sigma": "^3.0.1",
|
|
|
36 |
"tailwind-merge": "^3.0.1",
|
37 |
"zustand": "^5.0.3",
|
38 |
},
|
@@ -41,19 +48,19 @@
|
|
41 |
"@stylistic/eslint-plugin-js": "^3.1.0",
|
42 |
"@tailwindcss/vite": "^4.0.6",
|
43 |
"@types/bun": "^1.2.2",
|
44 |
-
"@types/node": "^22.13.
|
45 |
"@types/react": "^19.0.8",
|
46 |
"@types/react-dom": "^19.0.3",
|
47 |
"@types/seedrandom": "^3.0.8",
|
48 |
"@vitejs/plugin-react-swc": "^3.8.0",
|
49 |
-
"eslint": "^9.20.
|
50 |
"eslint-config-prettier": "^10.0.1",
|
51 |
"eslint-plugin-react": "^7.37.4",
|
52 |
"eslint-plugin-react-hooks": "^5.1.0",
|
53 |
"eslint-plugin-react-refresh": "^0.4.19",
|
54 |
-
"globals": "^15.
|
55 |
"graphology-types": "^0.24.8",
|
56 |
-
"prettier": "^3.5.
|
57 |
"prettier-plugin-tailwindcss": "^0.6.11",
|
58 |
"tailwindcss": "^4.0.6",
|
59 |
"tailwindcss-animate": "^1.0.7",
|
@@ -172,7 +179,7 @@
|
|
172 |
|
173 |
"@eslint/plugin-kit": ["@eslint/[email protected]", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="],
|
174 |
|
175 |
-
"@faker-js/faker": ["@faker-js/faker@9.
|
176 |
|
177 |
"@floating-ui/core": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="],
|
178 |
|
@@ -206,18 +213,24 @@
|
|
206 |
|
207 |
"@nodelib/fs.walk": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
208 |
|
|
|
|
|
209 |
"@radix-ui/primitive": ["@radix-ui/[email protected]", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
210 |
|
211 |
"@radix-ui/react-arrow": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="],
|
212 |
|
213 |
"@radix-ui/react-checkbox": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw=="],
|
214 |
|
|
|
|
|
215 |
"@radix-ui/react-compose-refs": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
216 |
|
217 |
"@radix-ui/react-context": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
218 |
|
219 |
"@radix-ui/react-dialog": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw=="],
|
220 |
|
|
|
|
|
221 |
"@radix-ui/react-dismissable-layer": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
|
222 |
|
223 |
"@radix-ui/react-focus-guards": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
|
@@ -236,10 +249,18 @@
|
|
236 |
|
237 |
"@radix-ui/react-primitive": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
238 |
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
"@radix-ui/react-separator": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ=="],
|
240 |
|
241 |
"@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
|
242 |
|
|
|
|
|
243 |
"@radix-ui/react-tooltip": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA=="],
|
244 |
|
245 |
"@radix-ui/react-use-callback-ref": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
@@ -384,7 +405,7 @@
|
|
384 |
|
385 |
"@types/json-schema": ["@types/[email protected]", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
386 |
|
387 |
-
"@types/node": ["@types/[email protected].
|
388 |
|
389 |
"@types/parse-json": ["@types/[email protected]", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
390 |
|
@@ -446,8 +467,14 @@
|
|
446 |
|
447 |
"async-function": ["[email protected]", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
448 |
|
|
|
|
|
|
|
|
|
449 |
"available-typed-arrays": ["[email protected]", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
450 |
|
|
|
|
|
451 |
"babel-plugin-macros": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
|
452 |
|
453 |
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
@@ -478,6 +505,8 @@
|
|
478 |
|
479 |
"color-name": ["[email protected]", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
480 |
|
|
|
|
|
481 |
"concat-map": ["[email protected]", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
482 |
|
483 |
"convert-source-map": ["[email protected]", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
@@ -502,6 +531,8 @@
|
|
502 |
|
503 |
"define-properties": ["[email protected]", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
504 |
|
|
|
|
|
505 |
"detect-libc": ["[email protected]", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
506 |
|
507 |
"detect-node-es": ["[email protected]", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
@@ -536,7 +567,7 @@
|
|
536 |
|
537 |
"escape-string-regexp": ["[email protected]", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
538 |
|
539 |
-
"eslint": ["[email protected].
|
540 |
|
541 |
"eslint-config-prettier": ["[email protected]", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "build/bin/cli.js" } }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="],
|
542 |
|
@@ -574,6 +605,8 @@
|
|
574 |
|
575 |
"file-entry-cache": ["[email protected]", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
576 |
|
|
|
|
|
577 |
"fill-range": ["[email protected]", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
578 |
|
579 |
"find-root": ["[email protected]", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
|
@@ -584,8 +617,12 @@
|
|
584 |
|
585 |
"flatted": ["[email protected]", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="],
|
586 |
|
|
|
|
|
587 |
"for-each": ["[email protected]", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw=="],
|
588 |
|
|
|
|
|
589 |
"fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
590 |
|
591 |
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
@@ -604,7 +641,7 @@
|
|
604 |
|
605 |
"glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
606 |
|
607 |
-
"globals": ["globals@15.
|
608 |
|
609 |
"globalthis": ["[email protected]", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
610 |
|
@@ -778,6 +815,10 @@
|
|
778 |
|
779 |
"micromatch": ["[email protected]", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
780 |
|
|
|
|
|
|
|
|
|
781 |
"minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
782 |
|
783 |
"minisearch": ["[email protected]", "", {}, "sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw=="],
|
@@ -838,12 +879,14 @@
|
|
838 |
|
839 |
"prelude-ls": ["[email protected]", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
840 |
|
841 |
-
"prettier": ["[email protected].
|
842 |
|
843 |
"prettier-plugin-tailwindcss": ["[email protected]", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA=="],
|
844 |
|
845 |
"prop-types": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
846 |
|
|
|
|
|
847 |
"punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
848 |
|
849 |
"queue-microtask": ["[email protected]", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
@@ -852,6 +895,8 @@
|
|
852 |
|
853 |
"react-dom": ["[email protected]", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="],
|
854 |
|
|
|
|
|
855 |
"react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
856 |
|
857 |
"react-remove-scroll": ["[email protected]", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
|
@@ -912,6 +957,8 @@
|
|
912 |
|
913 |
"sigma": ["[email protected]", "", { "dependencies": { "events": "^3.3.0", "graphology-utils": "^2.5.2" } }, "sha512-z67BX1FhIpD+wLs2WJ7QS2aR49TcSr3YaVZ2zU8cAc5jMiUYlSbeDp4EI6euBDUpm3/lzO4pfytP/gW4BhXWuA=="],
|
914 |
|
|
|
|
|
915 |
"source-map": ["[email protected]", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
|
916 |
|
917 |
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
@@ -1006,12 +1053,16 @@
|
|
1006 |
|
1007 |
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/[email protected]", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
1008 |
|
|
|
|
|
1009 |
"@typescript-eslint/typescript-estree/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
1010 |
|
1011 |
"@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
1012 |
|
1013 |
"babel-plugin-macros/resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
1014 |
|
|
|
|
|
1015 |
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
1016 |
|
1017 |
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
|
|
4 |
"": {
|
5 |
"name": "lightrag-webui",
|
6 |
"dependencies": {
|
7 |
+
"@faker-js/faker": "^9.5.0",
|
8 |
"@radix-ui/react-checkbox": "^1.1.4",
|
9 |
"@radix-ui/react-dialog": "^1.1.6",
|
10 |
"@radix-ui/react-popover": "^1.1.6",
|
11 |
+
"@radix-ui/react-progress": "^1.1.2",
|
12 |
+
"@radix-ui/react-scroll-area": "^1.2.3",
|
13 |
"@radix-ui/react-separator": "^1.1.2",
|
14 |
"@radix-ui/react-slot": "^1.1.2",
|
15 |
+
"@radix-ui/react-tabs": "^1.1.3",
|
16 |
"@radix-ui/react-tooltip": "^1.1.8",
|
17 |
+
"@radix-ui/react-use-controllable-state": "^1.1.0",
|
18 |
"@react-sigma/core": "^5.0.2",
|
19 |
"@react-sigma/graph-search": "^5.0.3",
|
20 |
"@react-sigma/layout-circlepack": "^5.0.2",
|
|
|
26 |
"@react-sigma/minimap": "^5.0.2",
|
27 |
"@sigma/edge-curve": "^3.1.0",
|
28 |
"@sigma/node-border": "^3.0.0",
|
29 |
+
"axios": "^1.7.9",
|
30 |
"class-variance-authority": "^0.7.1",
|
31 |
"clsx": "^2.1.1",
|
32 |
"cmdk": "^1.0.4",
|
|
|
36 |
"minisearch": "^7.1.1",
|
37 |
"react": "^19.0.0",
|
38 |
"react-dom": "^19.0.0",
|
39 |
+
"react-dropzone": "^14.3.5",
|
40 |
"seedrandom": "^3.0.5",
|
41 |
"sigma": "^3.0.1",
|
42 |
+
"sonner": "^1.7.4",
|
43 |
"tailwind-merge": "^3.0.1",
|
44 |
"zustand": "^5.0.3",
|
45 |
},
|
|
|
48 |
"@stylistic/eslint-plugin-js": "^3.1.0",
|
49 |
"@tailwindcss/vite": "^4.0.6",
|
50 |
"@types/bun": "^1.2.2",
|
51 |
+
"@types/node": "^22.13.4",
|
52 |
"@types/react": "^19.0.8",
|
53 |
"@types/react-dom": "^19.0.3",
|
54 |
"@types/seedrandom": "^3.0.8",
|
55 |
"@vitejs/plugin-react-swc": "^3.8.0",
|
56 |
+
"eslint": "^9.20.1",
|
57 |
"eslint-config-prettier": "^10.0.1",
|
58 |
"eslint-plugin-react": "^7.37.4",
|
59 |
"eslint-plugin-react-hooks": "^5.1.0",
|
60 |
"eslint-plugin-react-refresh": "^0.4.19",
|
61 |
+
"globals": "^15.15.0",
|
62 |
"graphology-types": "^0.24.8",
|
63 |
+
"prettier": "^3.5.1",
|
64 |
"prettier-plugin-tailwindcss": "^0.6.11",
|
65 |
"tailwindcss": "^4.0.6",
|
66 |
"tailwindcss-animate": "^1.0.7",
|
|
|
179 |
|
180 |
"@eslint/plugin-kit": ["@eslint/[email protected]", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="],
|
181 |
|
182 |
+
"@faker-js/faker": ["@faker-js/faker@9.5.0", "", {}, "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw=="],
|
183 |
|
184 |
"@floating-ui/core": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="],
|
185 |
|
|
|
213 |
|
214 |
"@nodelib/fs.walk": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
215 |
|
216 |
+
"@radix-ui/number": ["@radix-ui/[email protected]", "", {}, "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ=="],
|
217 |
+
|
218 |
"@radix-ui/primitive": ["@radix-ui/[email protected]", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
219 |
|
220 |
"@radix-ui/react-arrow": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="],
|
221 |
|
222 |
"@radix-ui/react-checkbox": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw=="],
|
223 |
|
224 |
+
"@radix-ui/react-collection": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw=="],
|
225 |
+
|
226 |
"@radix-ui/react-compose-refs": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
227 |
|
228 |
"@radix-ui/react-context": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
229 |
|
230 |
"@radix-ui/react-dialog": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw=="],
|
231 |
|
232 |
+
"@radix-ui/react-direction": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
233 |
+
|
234 |
"@radix-ui/react-dismissable-layer": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
|
235 |
|
236 |
"@radix-ui/react-focus-guards": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
|
|
|
249 |
|
250 |
"@radix-ui/react-primitive": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
251 |
|
252 |
+
"@radix-ui/react-progress": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA=="],
|
253 |
+
|
254 |
+
"@radix-ui/react-roving-focus": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw=="],
|
255 |
+
|
256 |
+
"@radix-ui/react-scroll-area": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ=="],
|
257 |
+
|
258 |
"@radix-ui/react-separator": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ=="],
|
259 |
|
260 |
"@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
|
261 |
|
262 |
+
"@radix-ui/react-tabs": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng=="],
|
263 |
+
|
264 |
"@radix-ui/react-tooltip": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA=="],
|
265 |
|
266 |
"@radix-ui/react-use-callback-ref": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
|
|
405 |
|
406 |
"@types/json-schema": ["@types/[email protected]", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
407 |
|
408 |
+
"@types/node": ["@types/[email protected].4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="],
|
409 |
|
410 |
"@types/parse-json": ["@types/[email protected]", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
411 |
|
|
|
467 |
|
468 |
"async-function": ["[email protected]", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
469 |
|
470 |
+
"asynckit": ["[email protected]", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
471 |
+
|
472 |
+
"attr-accept": ["[email protected]", "", {}, "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ=="],
|
473 |
+
|
474 |
"available-typed-arrays": ["[email protected]", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
475 |
|
476 |
+
"axios": ["[email protected]", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw=="],
|
477 |
+
|
478 |
"babel-plugin-macros": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
|
479 |
|
480 |
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
|
|
505 |
|
506 |
"color-name": ["[email protected]", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
507 |
|
508 |
+
"combined-stream": ["[email protected]", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
509 |
+
|
510 |
"concat-map": ["[email protected]", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
511 |
|
512 |
"convert-source-map": ["[email protected]", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
|
|
531 |
|
532 |
"define-properties": ["[email protected]", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
533 |
|
534 |
+
"delayed-stream": ["[email protected]", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
535 |
+
|
536 |
"detect-libc": ["[email protected]", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
537 |
|
538 |
"detect-node-es": ["[email protected]", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
|
|
567 |
|
568 |
"escape-string-regexp": ["[email protected]", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
569 |
|
570 |
+
"eslint": ["[email protected].1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g=="],
|
571 |
|
572 |
"eslint-config-prettier": ["[email protected]", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "build/bin/cli.js" } }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="],
|
573 |
|
|
|
605 |
|
606 |
"file-entry-cache": ["[email protected]", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
607 |
|
608 |
+
"file-selector": ["[email protected]", "", { "dependencies": { "tslib": "^2.7.0" } }, "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig=="],
|
609 |
+
|
610 |
"fill-range": ["[email protected]", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
611 |
|
612 |
"find-root": ["[email protected]", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
|
|
|
617 |
|
618 |
"flatted": ["[email protected]", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="],
|
619 |
|
620 |
+
"follow-redirects": ["[email protected]", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
|
621 |
+
|
622 |
"for-each": ["[email protected]", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw=="],
|
623 |
|
624 |
+
"form-data": ["[email protected]", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="],
|
625 |
+
|
626 |
"fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
627 |
|
628 |
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
|
641 |
|
642 |
"glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
643 |
|
644 |
+
"globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
|
645 |
|
646 |
"globalthis": ["[email protected]", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
647 |
|
|
|
815 |
|
816 |
"micromatch": ["[email protected]", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
817 |
|
818 |
+
"mime-db": ["[email protected]", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
819 |
+
|
820 |
+
"mime-types": ["[email protected]", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
821 |
+
|
822 |
"minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
823 |
|
824 |
"minisearch": ["[email protected]", "", {}, "sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw=="],
|
|
|
879 |
|
880 |
"prelude-ls": ["[email protected]", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
881 |
|
882 |
+
"prettier": ["[email protected].1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw=="],
|
883 |
|
884 |
"prettier-plugin-tailwindcss": ["[email protected]", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA=="],
|
885 |
|
886 |
"prop-types": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
887 |
|
888 |
+
"proxy-from-env": ["[email protected]", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
889 |
+
|
890 |
"punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
891 |
|
892 |
"queue-microtask": ["[email protected]", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
|
|
895 |
|
896 |
"react-dom": ["[email protected]", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="],
|
897 |
|
898 |
+
"react-dropzone": ["[email protected]", "", { "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">= 16.8 || 18.0.0" } }, "sha512-9nDUaEEpqZLOz5v5SUcFA0CjM4vq8YbqO0WRls+EYT7+DvxUdzDPKNCPLqGfj3YL9MsniCLCD4RFA6M95V6KMQ=="],
|
899 |
+
|
900 |
"react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
901 |
|
902 |
"react-remove-scroll": ["[email protected]", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
|
|
|
957 |
|
958 |
"sigma": ["[email protected]", "", { "dependencies": { "events": "^3.3.0", "graphology-utils": "^2.5.2" } }, "sha512-z67BX1FhIpD+wLs2WJ7QS2aR49TcSr3YaVZ2zU8cAc5jMiUYlSbeDp4EI6euBDUpm3/lzO4pfytP/gW4BhXWuA=="],
|
959 |
|
960 |
+
"sonner": ["[email protected]", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="],
|
961 |
+
|
962 |
"source-map": ["[email protected]", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
|
963 |
|
964 |
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
|
1053 |
|
1054 |
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/[email protected]", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
1055 |
|
1056 |
+
"@types/ws/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
|
1057 |
+
|
1058 |
"@typescript-eslint/typescript-estree/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
1059 |
|
1060 |
"@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
1061 |
|
1062 |
"babel-plugin-macros/resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
1063 |
|
1064 |
+
"bun-types/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
|
1065 |
+
|
1066 |
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
1067 |
|
1068 |
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
lightrag_webui/eslint.config.js
CHANGED
@@ -7,27 +7,31 @@ import tseslint from 'typescript-eslint'
|
|
7 |
import prettier from 'eslint-config-prettier'
|
8 |
import react from 'eslint-plugin-react'
|
9 |
|
10 |
-
export default tseslint.config(
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
32 |
}
|
33 |
-
|
|
|
7 |
import prettier from 'eslint-config-prettier'
|
8 |
import react from 'eslint-plugin-react'
|
9 |
|
10 |
+
export default tseslint.config(
|
11 |
+
{ ignores: ['dist'] },
|
12 |
+
{
|
13 |
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
14 |
+
files: ['**/*.{ts,tsx,js,jsx}'],
|
15 |
+
languageOptions: {
|
16 |
+
ecmaVersion: 2020,
|
17 |
+
globals: globals.browser
|
18 |
+
},
|
19 |
+
settings: { react: { version: '19.0' } },
|
20 |
+
plugins: {
|
21 |
+
'react-hooks': reactHooks,
|
22 |
+
'react-refresh': reactRefresh,
|
23 |
+
'@stylistic/js': stylisticJs,
|
24 |
+
react
|
25 |
+
},
|
26 |
+
rules: {
|
27 |
+
...reactHooks.configs.recommended.rules,
|
28 |
+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
29 |
+
...react.configs.recommended.rules,
|
30 |
+
...react.configs['jsx-runtime'].rules,
|
31 |
+
'@stylistic/js/indent': ['error', 2],
|
32 |
+
'@stylistic/js/quotes': ['error', 'single'],
|
33 |
+
'@typescript-eslint/no-explicit-any': ['off']
|
34 |
+
},
|
35 |
+
prettier
|
36 |
}
|
37 |
+
)
|
lightrag_webui/package.json
CHANGED
@@ -10,13 +10,17 @@
|
|
10 |
"preview": "bunx --bun vite preview"
|
11 |
},
|
12 |
"dependencies": {
|
13 |
-
"@faker-js/faker": "^9.
|
14 |
"@radix-ui/react-checkbox": "^1.1.4",
|
15 |
"@radix-ui/react-dialog": "^1.1.6",
|
16 |
"@radix-ui/react-popover": "^1.1.6",
|
|
|
|
|
17 |
"@radix-ui/react-separator": "^1.1.2",
|
18 |
"@radix-ui/react-slot": "^1.1.2",
|
|
|
19 |
"@radix-ui/react-tooltip": "^1.1.8",
|
|
|
20 |
"@react-sigma/core": "^5.0.2",
|
21 |
"@react-sigma/graph-search": "^5.0.3",
|
22 |
"@react-sigma/layout-circlepack": "^5.0.2",
|
@@ -28,6 +32,7 @@
|
|
28 |
"@react-sigma/minimap": "^5.0.2",
|
29 |
"@sigma/edge-curve": "^3.1.0",
|
30 |
"@sigma/node-border": "^3.0.0",
|
|
|
31 |
"class-variance-authority": "^0.7.1",
|
32 |
"clsx": "^2.1.1",
|
33 |
"cmdk": "^1.0.4",
|
@@ -37,8 +42,10 @@
|
|
37 |
"minisearch": "^7.1.1",
|
38 |
"react": "^19.0.0",
|
39 |
"react-dom": "^19.0.0",
|
|
|
40 |
"seedrandom": "^3.0.5",
|
41 |
"sigma": "^3.0.1",
|
|
|
42 |
"tailwind-merge": "^3.0.1",
|
43 |
"zustand": "^5.0.3"
|
44 |
},
|
@@ -47,19 +54,19 @@
|
|
47 |
"@stylistic/eslint-plugin-js": "^3.1.0",
|
48 |
"@tailwindcss/vite": "^4.0.6",
|
49 |
"@types/bun": "^1.2.2",
|
50 |
-
"@types/node": "^22.13.
|
51 |
"@types/react": "^19.0.8",
|
52 |
"@types/react-dom": "^19.0.3",
|
53 |
"@types/seedrandom": "^3.0.8",
|
54 |
"@vitejs/plugin-react-swc": "^3.8.0",
|
55 |
-
"eslint": "^9.20.
|
56 |
"eslint-config-prettier": "^10.0.1",
|
57 |
"eslint-plugin-react": "^7.37.4",
|
58 |
"eslint-plugin-react-hooks": "^5.1.0",
|
59 |
"eslint-plugin-react-refresh": "^0.4.19",
|
60 |
-
"globals": "^15.
|
61 |
"graphology-types": "^0.24.8",
|
62 |
-
"prettier": "^3.5.
|
63 |
"prettier-plugin-tailwindcss": "^0.6.11",
|
64 |
"tailwindcss": "^4.0.6",
|
65 |
"tailwindcss-animate": "^1.0.7",
|
|
|
10 |
"preview": "bunx --bun vite preview"
|
11 |
},
|
12 |
"dependencies": {
|
13 |
+
"@faker-js/faker": "^9.5.0",
|
14 |
"@radix-ui/react-checkbox": "^1.1.4",
|
15 |
"@radix-ui/react-dialog": "^1.1.6",
|
16 |
"@radix-ui/react-popover": "^1.1.6",
|
17 |
+
"@radix-ui/react-progress": "^1.1.2",
|
18 |
+
"@radix-ui/react-scroll-area": "^1.2.3",
|
19 |
"@radix-ui/react-separator": "^1.1.2",
|
20 |
"@radix-ui/react-slot": "^1.1.2",
|
21 |
+
"@radix-ui/react-tabs": "^1.1.3",
|
22 |
"@radix-ui/react-tooltip": "^1.1.8",
|
23 |
+
"@radix-ui/react-use-controllable-state": "^1.1.0",
|
24 |
"@react-sigma/core": "^5.0.2",
|
25 |
"@react-sigma/graph-search": "^5.0.3",
|
26 |
"@react-sigma/layout-circlepack": "^5.0.2",
|
|
|
32 |
"@react-sigma/minimap": "^5.0.2",
|
33 |
"@sigma/edge-curve": "^3.1.0",
|
34 |
"@sigma/node-border": "^3.0.0",
|
35 |
+
"axios": "^1.7.9",
|
36 |
"class-variance-authority": "^0.7.1",
|
37 |
"clsx": "^2.1.1",
|
38 |
"cmdk": "^1.0.4",
|
|
|
42 |
"minisearch": "^7.1.1",
|
43 |
"react": "^19.0.0",
|
44 |
"react-dom": "^19.0.0",
|
45 |
+
"react-dropzone": "^14.3.5",
|
46 |
"seedrandom": "^3.0.5",
|
47 |
"sigma": "^3.0.1",
|
48 |
+
"sonner": "^1.7.4",
|
49 |
"tailwind-merge": "^3.0.1",
|
50 |
"zustand": "^5.0.3"
|
51 |
},
|
|
|
54 |
"@stylistic/eslint-plugin-js": "^3.1.0",
|
55 |
"@tailwindcss/vite": "^4.0.6",
|
56 |
"@types/bun": "^1.2.2",
|
57 |
+
"@types/node": "^22.13.4",
|
58 |
"@types/react": "^19.0.8",
|
59 |
"@types/react-dom": "^19.0.3",
|
60 |
"@types/seedrandom": "^3.0.8",
|
61 |
"@vitejs/plugin-react-swc": "^3.8.0",
|
62 |
+
"eslint": "^9.20.1",
|
63 |
"eslint-config-prettier": "^10.0.1",
|
64 |
"eslint-plugin-react": "^7.37.4",
|
65 |
"eslint-plugin-react-hooks": "^5.1.0",
|
66 |
"eslint-plugin-react-refresh": "^0.4.19",
|
67 |
+
"globals": "^15.15.0",
|
68 |
"graphology-types": "^0.24.8",
|
69 |
+
"prettier": "^3.5.1",
|
70 |
"prettier-plugin-tailwindcss": "^0.6.11",
|
71 |
"tailwindcss": "^4.0.6",
|
72 |
"tailwindcss-animate": "^1.0.7",
|
lightrag_webui/src/App.tsx
CHANGED
@@ -1,11 +1,17 @@
|
|
1 |
import ThemeProvider from '@/components/ThemeProvider'
|
2 |
import MessageAlert from '@/components/MessageAlert'
|
3 |
import StatusIndicator from '@/components/StatusIndicator'
|
4 |
-
import GraphViewer from '@/GraphViewer'
|
5 |
import { healthCheckInterval } from '@/lib/constants'
|
6 |
import { useBackendState } from '@/stores/state'
|
7 |
import { useSettingsStore } from '@/stores/settings'
|
8 |
import { useEffect } from 'react'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
function App() {
|
11 |
const message = useBackendState.use.message()
|
@@ -26,11 +32,23 @@ function App() {
|
|
26 |
|
27 |
return (
|
28 |
<ThemeProvider>
|
29 |
-
<div className="h-screen w-screen">
|
30 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
</div>
|
32 |
{enableHealthCheck && <StatusIndicator />}
|
33 |
{message !== null && <MessageAlert />}
|
|
|
34 |
</ThemeProvider>
|
35 |
)
|
36 |
}
|
|
|
1 |
import ThemeProvider from '@/components/ThemeProvider'
|
2 |
import MessageAlert from '@/components/MessageAlert'
|
3 |
import StatusIndicator from '@/components/StatusIndicator'
|
|
|
4 |
import { healthCheckInterval } from '@/lib/constants'
|
5 |
import { useBackendState } from '@/stores/state'
|
6 |
import { useSettingsStore } from '@/stores/settings'
|
7 |
import { useEffect } from 'react'
|
8 |
+
import { Toaster } from 'sonner'
|
9 |
+
import SiteHeader from '@/features/SiteHeader'
|
10 |
+
|
11 |
+
import GraphViewer from '@/features/GraphViewer'
|
12 |
+
import DocumentManager from '@/features/DocumentManager'
|
13 |
+
|
14 |
+
import { Tabs, TabsContent } from '@/components/ui/Tabs'
|
15 |
|
16 |
function App() {
|
17 |
const message = useBackendState.use.message()
|
|
|
32 |
|
33 |
return (
|
34 |
<ThemeProvider>
|
35 |
+
<div className="flex h-screen w-screen">
|
36 |
+
<Tabs defaultValue="knowledge-graph" className="flex size-full flex-col">
|
37 |
+
<SiteHeader />
|
38 |
+
<TabsContent value="documents" className="flex-1">
|
39 |
+
<DocumentManager />
|
40 |
+
</TabsContent>
|
41 |
+
<TabsContent value="knowledge-graph" className="flex-1">
|
42 |
+
<GraphViewer />
|
43 |
+
</TabsContent>
|
44 |
+
<TabsContent value="settings" className="size-full">
|
45 |
+
<h1> Settings </h1>
|
46 |
+
</TabsContent>
|
47 |
+
</Tabs>
|
48 |
</div>
|
49 |
{enableHealthCheck && <StatusIndicator />}
|
50 |
{message !== null && <MessageAlert />}
|
51 |
+
<Toaster />
|
52 |
</ThemeProvider>
|
53 |
)
|
54 |
}
|
lightrag_webui/src/api/lightrag.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { backendBaseUrl } from '@/lib/constants'
|
2 |
import { errorMessage } from '@/lib/utils'
|
3 |
import { useSettingsStore } from '@/stores/settings'
|
@@ -64,81 +65,64 @@ export type QueryResponse = {
|
|
64 |
response: string
|
65 |
}
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
export const InvalidApiKeyError = 'Invalid API Key'
|
68 |
export const RequireApiKeError = 'API Key required'
|
69 |
|
70 |
-
//
|
71 |
-
const
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
const data = await response.json()
|
76 |
-
return JSON.stringify(data, undefined, 2)
|
77 |
-
} else if (contentType.startsWith('text/')) {
|
78 |
-
return await response.text()
|
79 |
-
} else if (contentType.includes('application/xml') || contentType.includes('text/xml')) {
|
80 |
-
return await response.text()
|
81 |
-
} else if (contentType.includes('application/octet-stream')) {
|
82 |
-
const buffer = await response.arrayBuffer()
|
83 |
-
const decoder = new TextDecoder('utf-8', { fatal: false, ignoreBOM: true })
|
84 |
-
return decoder.decode(buffer)
|
85 |
-
} else {
|
86 |
-
try {
|
87 |
-
return await response.text()
|
88 |
-
} catch (error) {
|
89 |
-
console.warn('Failed to decode as text, may be binary:', error)
|
90 |
-
return `[Could not decode response body. Content-Type: ${contentType}]`
|
91 |
-
}
|
92 |
-
}
|
93 |
-
} else {
|
94 |
-
try {
|
95 |
-
return await response.text()
|
96 |
-
} catch (error) {
|
97 |
-
console.warn('Failed to decode as text, may be binary:', error)
|
98 |
-
return '[Could not decode response body. No Content-Type header.]'
|
99 |
-
}
|
100 |
}
|
101 |
-
|
102 |
-
}
|
103 |
|
104 |
-
|
|
|
105 |
const apiKey = useSettingsStore.getState().apiKey
|
106 |
-
|
107 |
-
|
108 |
-
...(apiKey ? { 'X-API-Key': apiKey } : {})
|
109 |
}
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
120 |
}
|
121 |
-
|
122 |
-
return response
|
123 |
-
}
|
124 |
|
125 |
// API methods
|
126 |
export const queryGraphs = async (label: string): Promise<LightragGraphType> => {
|
127 |
-
const response = await
|
128 |
-
return
|
129 |
}
|
130 |
|
131 |
export const getGraphLabels = async (): Promise<string[]> => {
|
132 |
-
const response = await
|
133 |
-
return
|
134 |
}
|
135 |
|
136 |
export const checkHealth = async (): Promise<
|
137 |
LightragStatus | { status: 'error'; message: string }
|
138 |
> => {
|
139 |
try {
|
140 |
-
const response = await
|
141 |
-
return
|
142 |
} catch (e) {
|
143 |
return {
|
144 |
status: 'error',
|
@@ -148,63 +132,33 @@ export const checkHealth = async (): Promise<
|
|
148 |
}
|
149 |
|
150 |
export const getDocuments = async (): Promise<string[]> => {
|
151 |
-
const response = await
|
152 |
-
return
|
153 |
-
}
|
154 |
-
|
155 |
-
export const getDocumentsScanProgress = async (): Promise<LightragDocumentsScanProgress> => {
|
156 |
-
const response = await fetchWithAuth('/documents/scan-progress')
|
157 |
-
return await response.json()
|
158 |
}
|
159 |
|
160 |
-
export const
|
161 |
-
|
162 |
-
|
163 |
-
status: string
|
164 |
-
message: string
|
165 |
-
total_documents: number
|
166 |
-
}> => {
|
167 |
-
const formData = new FormData()
|
168 |
-
formData.append('file', file)
|
169 |
-
|
170 |
-
const response = await fetchWithAuth('/documents/upload', {
|
171 |
-
method: 'POST',
|
172 |
-
body: formData
|
173 |
-
})
|
174 |
-
return await response.json()
|
175 |
}
|
176 |
|
177 |
-
export const
|
178 |
-
const response = await
|
179 |
-
|
180 |
-
})
|
181 |
-
return await response.json()
|
182 |
}
|
183 |
|
184 |
export const queryText = async (request: QueryRequest): Promise<QueryResponse> => {
|
185 |
-
const response = await
|
186 |
-
|
187 |
-
headers: {
|
188 |
-
'Content-Type': 'application/json'
|
189 |
-
},
|
190 |
-
body: JSON.stringify(request)
|
191 |
-
})
|
192 |
-
return await response.json()
|
193 |
}
|
194 |
|
195 |
export const queryTextStream = async (request: QueryRequest, onChunk: (chunk: string) => void) => {
|
196 |
-
const response = await
|
197 |
-
|
198 |
-
headers: {
|
199 |
-
'Content-Type': 'application/json'
|
200 |
-
},
|
201 |
-
body: JSON.stringify(request)
|
202 |
})
|
203 |
|
204 |
-
const reader = response.
|
205 |
-
if (!reader) throw new Error('No response body')
|
206 |
-
|
207 |
const decoder = new TextDecoder()
|
|
|
208 |
while (true) {
|
209 |
const { done, value } = await reader.read()
|
210 |
if (done) break
|
@@ -226,53 +180,50 @@ export const queryTextStream = async (request: QueryRequest, onChunk: (chunk: st
|
|
226 |
}
|
227 |
}
|
228 |
|
229 |
-
// Text insertion API
|
230 |
export const insertText = async (
|
231 |
text: string,
|
232 |
description?: string
|
233 |
-
): Promise<{
|
234 |
-
|
235 |
-
|
236 |
-
document_count: number
|
237 |
-
}> => {
|
238 |
-
const response = await fetchWithAuth('/documents/text', {
|
239 |
-
method: 'POST',
|
240 |
-
headers: {
|
241 |
-
'Content-Type': 'application/json'
|
242 |
-
},
|
243 |
-
body: JSON.stringify({ text, description })
|
244 |
-
})
|
245 |
-
return await response.json()
|
246 |
}
|
247 |
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
): Promise<{
|
252 |
-
status: string
|
253 |
-
message: string
|
254 |
-
document_count: number
|
255 |
-
}> => {
|
256 |
const formData = new FormData()
|
257 |
-
|
258 |
-
formData.append('files', file)
|
259 |
-
})
|
260 |
|
261 |
-
const response = await
|
262 |
-
|
263 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
264 |
})
|
265 |
-
return
|
266 |
}
|
267 |
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
}
|
|
|
1 |
+
import axios, { AxiosError } from 'axios'
|
2 |
import { backendBaseUrl } from '@/lib/constants'
|
3 |
import { errorMessage } from '@/lib/utils'
|
4 |
import { useSettingsStore } from '@/stores/settings'
|
|
|
65 |
response: string
|
66 |
}
|
67 |
|
68 |
+
export type DocumentActionResponse = {
|
69 |
+
status: 'success' | 'partial_success' | 'failure'
|
70 |
+
message: string
|
71 |
+
document_count: number
|
72 |
+
}
|
73 |
+
|
74 |
export const InvalidApiKeyError = 'Invalid API Key'
|
75 |
export const RequireApiKeError = 'API Key required'
|
76 |
|
77 |
+
// Axios instance
|
78 |
+
const axiosInstance = axios.create({
|
79 |
+
baseURL: backendBaseUrl,
|
80 |
+
headers: {
|
81 |
+
'Content-Type': 'application/json'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
}
|
83 |
+
})
|
|
|
84 |
|
85 |
+
// Interceptor:add api key
|
86 |
+
axiosInstance.interceptors.request.use((config) => {
|
87 |
const apiKey = useSettingsStore.getState().apiKey
|
88 |
+
if (apiKey) {
|
89 |
+
config.headers['X-API-Key'] = apiKey
|
|
|
90 |
}
|
91 |
+
return config
|
92 |
+
})
|
93 |
+
|
94 |
+
// Interceptor:hanle error
|
95 |
+
axiosInstance.interceptors.response.use(
|
96 |
+
(response) => response,
|
97 |
+
(error: AxiosError) => {
|
98 |
+
if (error.response) {
|
99 |
+
throw new Error(
|
100 |
+
`${error.response.status} ${error.response.statusText}\n${JSON.stringify(
|
101 |
+
error.response.data
|
102 |
+
)}\n${error.config?.url}`
|
103 |
+
)
|
104 |
+
}
|
105 |
+
throw error
|
106 |
}
|
107 |
+
)
|
|
|
|
|
108 |
|
109 |
// API methods
|
110 |
export const queryGraphs = async (label: string): Promise<LightragGraphType> => {
|
111 |
+
const response = await axiosInstance.get(`/graphs?label=${label}`)
|
112 |
+
return response.data
|
113 |
}
|
114 |
|
115 |
export const getGraphLabels = async (): Promise<string[]> => {
|
116 |
+
const response = await axiosInstance.get('/graph/label/list')
|
117 |
+
return response.data
|
118 |
}
|
119 |
|
120 |
export const checkHealth = async (): Promise<
|
121 |
LightragStatus | { status: 'error'; message: string }
|
122 |
> => {
|
123 |
try {
|
124 |
+
const response = await axiosInstance.get('/health')
|
125 |
+
return response.data
|
126 |
} catch (e) {
|
127 |
return {
|
128 |
status: 'error',
|
|
|
132 |
}
|
133 |
|
134 |
export const getDocuments = async (): Promise<string[]> => {
|
135 |
+
const response = await axiosInstance.get('/documents')
|
136 |
+
return response.data
|
|
|
|
|
|
|
|
|
|
|
137 |
}
|
138 |
|
139 |
+
export const scanNewDocuments = async (): Promise<{ status: string }> => {
|
140 |
+
const response = await axiosInstance.post('/documents/scan')
|
141 |
+
return response.data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
}
|
143 |
|
144 |
+
export const getDocumentsScanProgress = async (): Promise<LightragDocumentsScanProgress> => {
|
145 |
+
const response = await axiosInstance.get('/documents/scan-progress')
|
146 |
+
return response.data
|
|
|
|
|
147 |
}
|
148 |
|
149 |
export const queryText = async (request: QueryRequest): Promise<QueryResponse> => {
|
150 |
+
const response = await axiosInstance.post('/query', request)
|
151 |
+
return response.data
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
}
|
153 |
|
154 |
export const queryTextStream = async (request: QueryRequest, onChunk: (chunk: string) => void) => {
|
155 |
+
const response = await axiosInstance.post('/query/stream', request, {
|
156 |
+
responseType: 'stream'
|
|
|
|
|
|
|
|
|
157 |
})
|
158 |
|
159 |
+
const reader = response.data.getReader()
|
|
|
|
|
160 |
const decoder = new TextDecoder()
|
161 |
+
|
162 |
while (true) {
|
163 |
const { done, value } = await reader.read()
|
164 |
if (done) break
|
|
|
180 |
}
|
181 |
}
|
182 |
|
|
|
183 |
export const insertText = async (
|
184 |
text: string,
|
185 |
description?: string
|
186 |
+
): Promise<DocumentActionResponse> => {
|
187 |
+
const response = await axiosInstance.post('/documents/text', { text, description })
|
188 |
+
return response.data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
}
|
190 |
|
191 |
+
export const uploadDocument = async (
|
192 |
+
file: File,
|
193 |
+
onUploadProgress?: (percentCompleted: number) => void
|
194 |
+
): Promise<DocumentActionResponse> => {
|
|
|
|
|
|
|
|
|
195 |
const formData = new FormData()
|
196 |
+
formData.append('file', file)
|
|
|
|
|
197 |
|
198 |
+
const response = await axiosInstance.post('/documents/upload', formData, {
|
199 |
+
headers: {
|
200 |
+
'Content-Type': 'multipart/form-data'
|
201 |
+
},
|
202 |
+
onUploadProgress:
|
203 |
+
onUploadProgress !== undefined
|
204 |
+
? (progressEvent) => {
|
205 |
+
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total!)
|
206 |
+
onUploadProgress(percentCompleted)
|
207 |
+
}
|
208 |
+
: undefined
|
209 |
})
|
210 |
+
return response.data
|
211 |
}
|
212 |
|
213 |
+
export const batchUploadDocuments = async (
|
214 |
+
files: File[],
|
215 |
+
onUploadProgress?: (fileName: string, percentCompleted: number) => void
|
216 |
+
): Promise<DocumentActionResponse[]> => {
|
217 |
+
return await Promise.all(
|
218 |
+
files.map(async (file) => {
|
219 |
+
return await uploadDocument(file, (percentCompleted) => {
|
220 |
+
onUploadProgress?.(file.name, percentCompleted)
|
221 |
+
})
|
222 |
+
})
|
223 |
+
)
|
224 |
+
}
|
225 |
+
|
226 |
+
export const clearDocuments = async (): Promise<DocumentActionResponse> => {
|
227 |
+
const response = await axiosInstance.delete('/documents')
|
228 |
+
return response.data
|
229 |
}
|
lightrag_webui/src/components/MessageAlert.tsx
CHANGED
@@ -22,10 +22,11 @@ const MessageAlert = () => {
|
|
22 |
|
23 |
return (
|
24 |
<Alert
|
25 |
-
variant={health ? 'default' : 'destructive'}
|
26 |
className={cn(
|
27 |
-
'bg-background/90 absolute top-
|
28 |
-
isMounted ? 'translate-y-0 opacity-100' : '-translate-y-20 opacity-0'
|
|
|
29 |
)}
|
30 |
>
|
31 |
{!health && (
|
@@ -42,7 +43,7 @@ const MessageAlert = () => {
|
|
42 |
<Button
|
43 |
size="sm"
|
44 |
variant={controlButtonVariant}
|
45 |
-
className="
|
46 |
onClick={() => useBackendState.getState().clear()}
|
47 |
>
|
48 |
Close
|
|
|
22 |
|
23 |
return (
|
24 |
<Alert
|
25 |
+
// variant={health ? 'default' : 'destructive'}
|
26 |
className={cn(
|
27 |
+
'bg-background/90 absolute top-12 left-1/2 flex w-auto max-w-lg -translate-x-1/2 transform items-center gap-4 shadow-md backdrop-blur-lg transition-all duration-500 ease-in-out',
|
28 |
+
isMounted ? 'translate-y-0 opacity-100' : '-translate-y-20 opacity-0',
|
29 |
+
!health && 'bg-red-700 text-white'
|
30 |
)}
|
31 |
>
|
32 |
{!health && (
|
|
|
43 |
<Button
|
44 |
size="sm"
|
45 |
variant={controlButtonVariant}
|
46 |
+
className="border-primary max-h-8 border !p-2 text-xs"
|
47 |
onClick={() => useBackendState.getState().clear()}
|
48 |
>
|
49 |
Close
|
lightrag_webui/src/components/PropertiesView.tsx
CHANGED
@@ -200,7 +200,7 @@ const EdgePropertiesView = ({ edge }: { edge: EdgeType }) => {
|
|
200 |
<label className="text-md pl-1 font-bold tracking-wide text-teal-600">Relationship</label>
|
201 |
<div className="bg-primary/5 max-h-96 overflow-auto rounded p-1">
|
202 |
<PropertyRow name={'Id'} value={edge.id} />
|
203 |
-
<PropertyRow name={'Type'} value={edge.type} />
|
204 |
<PropertyRow
|
205 |
name={'Source'}
|
206 |
value={edge.sourceNode ? edge.sourceNode.labels.join(', ') : edge.source}
|
|
|
200 |
<label className="text-md pl-1 font-bold tracking-wide text-teal-600">Relationship</label>
|
201 |
<div className="bg-primary/5 max-h-96 overflow-auto rounded p-1">
|
202 |
<PropertyRow name={'Id'} value={edge.id} />
|
203 |
+
{edge.type && <PropertyRow name={'Type'} value={edge.type} />}
|
204 |
<PropertyRow
|
205 |
name={'Source'}
|
206 |
value={edge.sourceNode ? edge.sourceNode.labels.join(', ') : edge.source}
|
lightrag_webui/src/components/document/ClearDocumentsDialog.tsx
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useCallback } from 'react'
|
2 |
+
import Button from '@/components/ui/Button'
|
3 |
+
import {
|
4 |
+
Dialog,
|
5 |
+
DialogContent,
|
6 |
+
DialogDescription,
|
7 |
+
DialogHeader,
|
8 |
+
DialogTitle,
|
9 |
+
DialogTrigger
|
10 |
+
} from '@/components/ui/Dialog'
|
11 |
+
import { toast } from 'sonner'
|
12 |
+
import { errorMessage } from '@/lib/utils'
|
13 |
+
import { clearDocuments } from '@/api/lightrag'
|
14 |
+
|
15 |
+
import { EraserIcon } from 'lucide-react'
|
16 |
+
|
17 |
+
export default function ClearDocumentsDialog() {
|
18 |
+
const [open, setOpen] = useState(false) // 添加状态控制
|
19 |
+
|
20 |
+
const handleClear = useCallback(async () => {
|
21 |
+
try {
|
22 |
+
const result = await clearDocuments()
|
23 |
+
if (result.status === 'success') {
|
24 |
+
toast.success('Documents cleared successfully')
|
25 |
+
setOpen(false)
|
26 |
+
} else {
|
27 |
+
toast.error(`Clear Documents Failed:\n${result.message}`)
|
28 |
+
}
|
29 |
+
} catch (err) {
|
30 |
+
toast.error('Clear Documents Failed:\n' + errorMessage(err))
|
31 |
+
}
|
32 |
+
}, [setOpen])
|
33 |
+
|
34 |
+
return (
|
35 |
+
<Dialog open={open} onOpenChange={setOpen}>
|
36 |
+
<DialogTrigger asChild>
|
37 |
+
<Button variant="outline" tooltip="Clear documents" side="bottom" size="icon">
|
38 |
+
<EraserIcon />
|
39 |
+
</Button>
|
40 |
+
</DialogTrigger>
|
41 |
+
<DialogContent className="sm:max-w-xl" onCloseAutoFocus={(e) => e.preventDefault()}>
|
42 |
+
<DialogHeader>
|
43 |
+
<DialogTitle>Clear documents</DialogTitle>
|
44 |
+
<DialogDescription>Do you really want to clear all documents?</DialogDescription>
|
45 |
+
</DialogHeader>
|
46 |
+
<Button variant="destructive" onClick={handleClear}>
|
47 |
+
YES
|
48 |
+
</Button>
|
49 |
+
</DialogContent>
|
50 |
+
</Dialog>
|
51 |
+
)
|
52 |
+
}
|
lightrag_webui/src/components/document/UploadDocumentsDialog.tsx
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useCallback } from 'react'
|
2 |
+
import Button from '@/components/ui/Button'
|
3 |
+
import {
|
4 |
+
Dialog,
|
5 |
+
DialogContent,
|
6 |
+
DialogDescription,
|
7 |
+
DialogHeader,
|
8 |
+
DialogTitle,
|
9 |
+
DialogTrigger
|
10 |
+
} from '@/components/ui/Dialog'
|
11 |
+
import FileUploader from '@/components/ui/FileUploader'
|
12 |
+
import { toast } from 'sonner'
|
13 |
+
import { errorMessage } from '@/lib/utils'
|
14 |
+
import { uploadDocument } from '@/api/lightrag'
|
15 |
+
|
16 |
+
import { UploadIcon } from 'lucide-react'
|
17 |
+
|
18 |
+
export default function UploadDocumentsDialog() {
|
19 |
+
const [open, setOpen] = useState(false)
|
20 |
+
const [isUploading, setIsUploading] = useState(false)
|
21 |
+
const [progresses, setProgresses] = useState<Record<string, number>>({})
|
22 |
+
|
23 |
+
const handleDocumentsUpload = useCallback(
|
24 |
+
async (filesToUpload: File[]) => {
|
25 |
+
setIsUploading(true)
|
26 |
+
|
27 |
+
try {
|
28 |
+
await Promise.all(
|
29 |
+
filesToUpload.map(async (file) => {
|
30 |
+
try {
|
31 |
+
const result = await uploadDocument(file, (percentCompleted: number) => {
|
32 |
+
console.debug(`Uploading ${file.name}: ${percentCompleted}%`)
|
33 |
+
setProgresses((pre) => ({
|
34 |
+
...pre,
|
35 |
+
[file.name]: percentCompleted
|
36 |
+
}))
|
37 |
+
})
|
38 |
+
if (result.status === 'success') {
|
39 |
+
toast.success(`Upload Success:\n${file.name} uploaded successfully`)
|
40 |
+
} else {
|
41 |
+
toast.error(`Upload Failed:\n${file.name}\n${result.message}`)
|
42 |
+
}
|
43 |
+
} catch (err) {
|
44 |
+
toast.error(`Upload Failed:\n${file.name}\n${errorMessage(err)}`)
|
45 |
+
}
|
46 |
+
})
|
47 |
+
)
|
48 |
+
} catch (err) {
|
49 |
+
toast.error('Upload Failed\n' + errorMessage(err))
|
50 |
+
} finally {
|
51 |
+
setIsUploading(false)
|
52 |
+
setOpen(false)
|
53 |
+
}
|
54 |
+
},
|
55 |
+
[setIsUploading, setProgresses, setOpen]
|
56 |
+
)
|
57 |
+
|
58 |
+
return (
|
59 |
+
<Dialog
|
60 |
+
open={open}
|
61 |
+
onOpenChange={(open) => {
|
62 |
+
if (isUploading && !open) {
|
63 |
+
return
|
64 |
+
}
|
65 |
+
setOpen(open)
|
66 |
+
}}
|
67 |
+
>
|
68 |
+
<DialogTrigger asChild>
|
69 |
+
<Button variant="outline" tooltip="Upload documents" side="bottom" size="icon">
|
70 |
+
<UploadIcon />
|
71 |
+
</Button>
|
72 |
+
</DialogTrigger>
|
73 |
+
<DialogContent className="sm:max-w-xl" onCloseAutoFocus={(e) => e.preventDefault()}>
|
74 |
+
<DialogHeader>
|
75 |
+
<DialogTitle>Upload documents</DialogTitle>
|
76 |
+
<DialogDescription>
|
77 |
+
Drag and drop your documents here or click to browse.
|
78 |
+
</DialogDescription>
|
79 |
+
</DialogHeader>
|
80 |
+
<FileUploader
|
81 |
+
maxFileCount={Infinity}
|
82 |
+
maxSize={200 * 1024 * 1024}
|
83 |
+
description="supported types: TXT, MD, DOC, PDF, PPTX"
|
84 |
+
onUpload={handleDocumentsUpload}
|
85 |
+
progresses={progresses}
|
86 |
+
disabled={isUploading}
|
87 |
+
/>
|
88 |
+
</DialogContent>
|
89 |
+
</Dialog>
|
90 |
+
)
|
91 |
+
}
|
lightrag_webui/src/components/ui/AsyncSearch.tsx
CHANGED
@@ -193,7 +193,7 @@ export function AsyncSearch<T>({
|
|
193 |
</div>
|
194 |
)}
|
195 |
</div>
|
196 |
-
<CommandList
|
197 |
{error && <div className="text-destructive p-4 text-center">{error}</div>}
|
198 |
{loading && options.length === 0 && (loadingSkeleton || <DefaultLoadingSkeleton />)}
|
199 |
{!loading &&
|
|
|
193 |
</div>
|
194 |
)}
|
195 |
</div>
|
196 |
+
<CommandList hidden={!open || debouncedSearchTerm.length === 0}>
|
197 |
{error && <div className="text-destructive p-4 text-center">{error}</div>}
|
198 |
{loading && options.length === 0 && (loadingSkeleton || <DefaultLoadingSkeleton />)}
|
199 |
{!loading &&
|
lightrag_webui/src/components/ui/Badge.tsx
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import { cva, type VariantProps } from 'class-variance-authority'
|
3 |
+
|
4 |
+
import { cn } from '@/lib/utils'
|
5 |
+
|
6 |
+
const badgeVariants = cva(
|
7 |
+
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
8 |
+
{
|
9 |
+
variants: {
|
10 |
+
variant: {
|
11 |
+
default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
12 |
+
secondary:
|
13 |
+
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
14 |
+
destructive:
|
15 |
+
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
16 |
+
outline: 'text-foreground'
|
17 |
+
}
|
18 |
+
},
|
19 |
+
defaultVariants: {
|
20 |
+
variant: 'default'
|
21 |
+
}
|
22 |
+
}
|
23 |
+
)
|
24 |
+
|
25 |
+
export interface BadgeProps
|
26 |
+
extends React.HTMLAttributes<HTMLDivElement>,
|
27 |
+
VariantProps<typeof badgeVariants> {}
|
28 |
+
|
29 |
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
30 |
+
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
|
31 |
+
}
|
32 |
+
|
33 |
+
export default Badge
|
lightrag_webui/src/components/ui/Card.tsx
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
|
3 |
+
import { cn } from '@/lib/utils'
|
4 |
+
|
5 |
+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
6 |
+
({ className, ...props }, ref) => (
|
7 |
+
<div
|
8 |
+
ref={ref}
|
9 |
+
className={cn('bg-card text-card-foreground rounded-xl border shadow', className)}
|
10 |
+
{...props}
|
11 |
+
/>
|
12 |
+
)
|
13 |
+
)
|
14 |
+
Card.displayName = 'Card'
|
15 |
+
|
16 |
+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
17 |
+
({ className, ...props }, ref) => (
|
18 |
+
<div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
19 |
+
)
|
20 |
+
)
|
21 |
+
CardHeader.displayName = 'CardHeader'
|
22 |
+
|
23 |
+
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
24 |
+
({ className, ...props }, ref) => (
|
25 |
+
<div
|
26 |
+
ref={ref}
|
27 |
+
className={cn('leading-none font-semibold tracking-tight', className)}
|
28 |
+
{...props}
|
29 |
+
/>
|
30 |
+
)
|
31 |
+
)
|
32 |
+
CardTitle.displayName = 'CardTitle'
|
33 |
+
|
34 |
+
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
35 |
+
({ className, ...props }, ref) => (
|
36 |
+
<div ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props} />
|
37 |
+
)
|
38 |
+
)
|
39 |
+
CardDescription.displayName = 'CardDescription'
|
40 |
+
|
41 |
+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
42 |
+
({ className, ...props }, ref) => (
|
43 |
+
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
44 |
+
)
|
45 |
+
)
|
46 |
+
CardContent.displayName = 'CardContent'
|
47 |
+
|
48 |
+
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
49 |
+
({ className, ...props }, ref) => (
|
50 |
+
<div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
|
51 |
+
)
|
52 |
+
)
|
53 |
+
CardFooter.displayName = 'CardFooter'
|
54 |
+
|
55 |
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
lightrag_webui/src/components/ui/DataTable.tsx
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
2 |
+
|
3 |
+
import {
|
4 |
+
Table,
|
5 |
+
TableBody,
|
6 |
+
TableCell,
|
7 |
+
TableHead,
|
8 |
+
TableHeader,
|
9 |
+
TableRow
|
10 |
+
} from '@/components/ui/Table'
|
11 |
+
|
12 |
+
interface DataTableProps<TData, TValue> {
|
13 |
+
columns: ColumnDef<TData, TValue>[]
|
14 |
+
data: TData[]
|
15 |
+
}
|
16 |
+
|
17 |
+
export default function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData, TValue>) {
|
18 |
+
const table = useReactTable({
|
19 |
+
data,
|
20 |
+
columns,
|
21 |
+
getCoreRowModel: getCoreRowModel()
|
22 |
+
})
|
23 |
+
|
24 |
+
return (
|
25 |
+
<div className="rounded-md border">
|
26 |
+
<Table>
|
27 |
+
<TableHeader>
|
28 |
+
{table.getHeaderGroups().map((headerGroup) => (
|
29 |
+
<TableRow key={headerGroup.id}>
|
30 |
+
{headerGroup.headers.map((header) => {
|
31 |
+
return (
|
32 |
+
<TableHead key={header.id}>
|
33 |
+
{header.isPlaceholder
|
34 |
+
? null
|
35 |
+
: flexRender(header.column.columnDef.header, header.getContext())}
|
36 |
+
</TableHead>
|
37 |
+
)
|
38 |
+
})}
|
39 |
+
</TableRow>
|
40 |
+
))}
|
41 |
+
</TableHeader>
|
42 |
+
<TableBody>
|
43 |
+
{table.getRowModel().rows?.length ? (
|
44 |
+
table.getRowModel().rows.map((row) => (
|
45 |
+
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
|
46 |
+
{row.getVisibleCells().map((cell) => (
|
47 |
+
<TableCell key={cell.id}>
|
48 |
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
49 |
+
</TableCell>
|
50 |
+
))}
|
51 |
+
</TableRow>
|
52 |
+
))
|
53 |
+
) : (
|
54 |
+
<TableRow>
|
55 |
+
<TableCell colSpan={columns.length} className="h-24 text-center">
|
56 |
+
No results.
|
57 |
+
</TableCell>
|
58 |
+
</TableRow>
|
59 |
+
)}
|
60 |
+
</TableBody>
|
61 |
+
</Table>
|
62 |
+
</div>
|
63 |
+
)
|
64 |
+
}
|
lightrag_webui/src/components/ui/Dialog.tsx
CHANGED
@@ -36,7 +36,7 @@ const DialogContent = React.forwardRef<
|
|
36 |
<DialogPrimitive.Content
|
37 |
ref={ref}
|
38 |
className={cn(
|
39 |
-
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-
|
40 |
className
|
41 |
)}
|
42 |
{...props}
|
@@ -65,7 +65,7 @@ const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivEleme
|
|
65 |
DialogFooter.displayName = 'DialogFooter'
|
66 |
|
67 |
const DialogTitle = React.forwardRef<
|
68 |
-
React.
|
69 |
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
70 |
>(({ className, ...props }, ref) => (
|
71 |
<DialogPrimitive.Title
|
@@ -77,7 +77,7 @@ const DialogTitle = React.forwardRef<
|
|
77 |
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
78 |
|
79 |
const DialogDescription = React.forwardRef<
|
80 |
-
React.
|
81 |
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
82 |
>(({ className, ...props }, ref) => (
|
83 |
<DialogPrimitive.Description
|
|
|
36 |
<DialogPrimitive.Content
|
37 |
ref={ref}
|
38 |
className={cn(
|
39 |
+
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg',
|
40 |
className
|
41 |
)}
|
42 |
{...props}
|
|
|
65 |
DialogFooter.displayName = 'DialogFooter'
|
66 |
|
67 |
const DialogTitle = React.forwardRef<
|
68 |
+
React.ComponentRef<typeof DialogPrimitive.Title>,
|
69 |
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
70 |
>(({ className, ...props }, ref) => (
|
71 |
<DialogPrimitive.Title
|
|
|
77 |
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
78 |
|
79 |
const DialogDescription = React.forwardRef<
|
80 |
+
React.ComponentRef<typeof DialogPrimitive.Description>,
|
81 |
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
82 |
>(({ className, ...props }, ref) => (
|
83 |
<DialogPrimitive.Description
|
lightrag_webui/src/components/ui/EmptyCard.tsx
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { cn } from '@/lib/utils'
|
2 |
+
import { Card, CardDescription, CardTitle } from '@/components/ui/Card'
|
3 |
+
import { FilesIcon } from 'lucide-react'
|
4 |
+
|
5 |
+
interface EmptyCardProps extends React.ComponentPropsWithoutRef<typeof Card> {
|
6 |
+
title: string
|
7 |
+
description?: string
|
8 |
+
action?: React.ReactNode
|
9 |
+
icon?: React.ComponentType<{ className?: string }>
|
10 |
+
}
|
11 |
+
|
12 |
+
export default function EmptyCard({
|
13 |
+
title,
|
14 |
+
description,
|
15 |
+
icon: Icon = FilesIcon,
|
16 |
+
action,
|
17 |
+
className,
|
18 |
+
...props
|
19 |
+
}: EmptyCardProps) {
|
20 |
+
return (
|
21 |
+
<Card
|
22 |
+
className={cn(
|
23 |
+
'flex w-full flex-col items-center justify-center space-y-6 bg-transparent p-16',
|
24 |
+
className
|
25 |
+
)}
|
26 |
+
{...props}
|
27 |
+
>
|
28 |
+
<div className="mr-4 shrink-0 rounded-full border border-dashed p-4">
|
29 |
+
<Icon className="text-muted-foreground size-8" aria-hidden="true" />
|
30 |
+
</div>
|
31 |
+
<div className="flex flex-col items-center gap-1.5 text-center">
|
32 |
+
<CardTitle>{title}</CardTitle>
|
33 |
+
{description ? <CardDescription>{description}</CardDescription> : null}
|
34 |
+
</div>
|
35 |
+
{action ? action : null}
|
36 |
+
</Card>
|
37 |
+
)
|
38 |
+
}
|
lightrag_webui/src/components/ui/FileUploader.tsx
ADDED
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* @see https://github.com/sadmann7/file-uploader
|
3 |
+
*/
|
4 |
+
|
5 |
+
import * as React from 'react'
|
6 |
+
import { FileText, Upload, X } from 'lucide-react'
|
7 |
+
import Dropzone, { type DropzoneProps, type FileRejection } from 'react-dropzone'
|
8 |
+
import { toast } from 'sonner'
|
9 |
+
|
10 |
+
import { cn } from '@/lib/utils'
|
11 |
+
import { useControllableState } from '@radix-ui/react-use-controllable-state'
|
12 |
+
import Button from '@/components/ui/Button'
|
13 |
+
import Progress from '@/components/ui/Progress'
|
14 |
+
import { ScrollArea } from '@/components/ui/ScrollArea'
|
15 |
+
import { supportedFileTypes } from '@/lib/constants'
|
16 |
+
|
17 |
+
interface FileUploaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
18 |
+
/**
|
19 |
+
* Value of the uploader.
|
20 |
+
* @type File[]
|
21 |
+
* @default undefined
|
22 |
+
* @example value={files}
|
23 |
+
*/
|
24 |
+
value?: File[]
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Function to be called when the value changes.
|
28 |
+
* @type (files: File[]) => void
|
29 |
+
* @default undefined
|
30 |
+
* @example onValueChange={(files) => setFiles(files)}
|
31 |
+
*/
|
32 |
+
onValueChange?: (files: File[]) => void
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Function to be called when files are uploaded.
|
36 |
+
* @type (files: File[]) => Promise<void>
|
37 |
+
* @default undefined
|
38 |
+
* @example onUpload={(files) => uploadFiles(files)}
|
39 |
+
*/
|
40 |
+
onUpload?: (files: File[]) => Promise<void>
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Progress of the uploaded files.
|
44 |
+
* @type Record<string, number> | undefined
|
45 |
+
* @default undefined
|
46 |
+
* @example progresses={{ "file1.png": 50 }}
|
47 |
+
*/
|
48 |
+
progresses?: Record<string, number>
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Accepted file types for the uploader.
|
52 |
+
* @type { [key: string]: string[]}
|
53 |
+
* @default
|
54 |
+
* ```ts
|
55 |
+
* { "text/*": [] }
|
56 |
+
* ```
|
57 |
+
* @example accept={["text/plain", "application/pdf"]}
|
58 |
+
*/
|
59 |
+
accept?: DropzoneProps['accept']
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Maximum file size for the uploader.
|
63 |
+
* @type number | undefined
|
64 |
+
* @default 1024 * 1024 * 200 // 200MB
|
65 |
+
* @example maxSize={1024 * 1024 * 2} // 2MB
|
66 |
+
*/
|
67 |
+
maxSize?: DropzoneProps['maxSize']
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Maximum number of files for the uploader.
|
71 |
+
* @type number | undefined
|
72 |
+
* @default 1
|
73 |
+
* @example maxFileCount={4}
|
74 |
+
*/
|
75 |
+
maxFileCount?: DropzoneProps['maxFiles']
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Whether the uploader should accept multiple files.
|
79 |
+
* @type boolean
|
80 |
+
* @default false
|
81 |
+
* @example multiple
|
82 |
+
*/
|
83 |
+
multiple?: boolean
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Whether the uploader is disabled.
|
87 |
+
* @type boolean
|
88 |
+
* @default false
|
89 |
+
* @example disabled
|
90 |
+
*/
|
91 |
+
disabled?: boolean
|
92 |
+
|
93 |
+
description?: string
|
94 |
+
}
|
95 |
+
|
96 |
+
function formatBytes(
|
97 |
+
bytes: number,
|
98 |
+
opts: {
|
99 |
+
decimals?: number
|
100 |
+
sizeType?: 'accurate' | 'normal'
|
101 |
+
} = {}
|
102 |
+
) {
|
103 |
+
const { decimals = 0, sizeType = 'normal' } = opts
|
104 |
+
|
105 |
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
|
106 |
+
const accurateSizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB']
|
107 |
+
if (bytes === 0) return '0 Byte'
|
108 |
+
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
109 |
+
return `${(bytes / Math.pow(1024, i)).toFixed(decimals)} ${
|
110 |
+
sizeType === 'accurate' ? (accurateSizes[i] ?? 'Bytes') : (sizes[i] ?? 'Bytes')
|
111 |
+
}`
|
112 |
+
}
|
113 |
+
|
114 |
+
function FileUploader(props: FileUploaderProps) {
|
115 |
+
const {
|
116 |
+
value: valueProp,
|
117 |
+
onValueChange,
|
118 |
+
onUpload,
|
119 |
+
progresses,
|
120 |
+
accept = supportedFileTypes,
|
121 |
+
maxSize = 1024 * 1024 * 200,
|
122 |
+
maxFileCount = 1,
|
123 |
+
multiple = false,
|
124 |
+
disabled = false,
|
125 |
+
description,
|
126 |
+
className,
|
127 |
+
...dropzoneProps
|
128 |
+
} = props
|
129 |
+
|
130 |
+
const [files, setFiles] = useControllableState({
|
131 |
+
prop: valueProp,
|
132 |
+
onChange: onValueChange
|
133 |
+
})
|
134 |
+
|
135 |
+
const onDrop = React.useCallback(
|
136 |
+
(acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
|
137 |
+
if (!multiple && maxFileCount === 1 && acceptedFiles.length > 1) {
|
138 |
+
toast.error('Cannot upload more than 1 file at a time')
|
139 |
+
return
|
140 |
+
}
|
141 |
+
|
142 |
+
if ((files?.length ?? 0) + acceptedFiles.length > maxFileCount) {
|
143 |
+
toast.error(`Cannot upload more than ${maxFileCount} files`)
|
144 |
+
return
|
145 |
+
}
|
146 |
+
|
147 |
+
const newFiles = acceptedFiles.map((file) =>
|
148 |
+
Object.assign(file, {
|
149 |
+
preview: URL.createObjectURL(file)
|
150 |
+
})
|
151 |
+
)
|
152 |
+
|
153 |
+
const updatedFiles = files ? [...files, ...newFiles] : newFiles
|
154 |
+
|
155 |
+
setFiles(updatedFiles)
|
156 |
+
|
157 |
+
if (rejectedFiles.length > 0) {
|
158 |
+
rejectedFiles.forEach(({ file }) => {
|
159 |
+
toast.error(`File ${file.name} was rejected`)
|
160 |
+
})
|
161 |
+
}
|
162 |
+
|
163 |
+
if (onUpload && updatedFiles.length > 0 && updatedFiles.length <= maxFileCount) {
|
164 |
+
const target = updatedFiles.length > 0 ? `${updatedFiles.length} files` : 'file'
|
165 |
+
|
166 |
+
toast.promise(onUpload(updatedFiles), {
|
167 |
+
loading: `Uploading ${target}...`,
|
168 |
+
success: () => {
|
169 |
+
setFiles([])
|
170 |
+
return `${target} uploaded`
|
171 |
+
},
|
172 |
+
error: `Failed to upload ${target}`
|
173 |
+
})
|
174 |
+
}
|
175 |
+
},
|
176 |
+
|
177 |
+
[files, maxFileCount, multiple, onUpload, setFiles]
|
178 |
+
)
|
179 |
+
|
180 |
+
function onRemove(index: number) {
|
181 |
+
if (!files) return
|
182 |
+
const newFiles = files.filter((_, i) => i !== index)
|
183 |
+
setFiles(newFiles)
|
184 |
+
onValueChange?.(newFiles)
|
185 |
+
}
|
186 |
+
|
187 |
+
// Revoke preview url when component unmounts
|
188 |
+
React.useEffect(() => {
|
189 |
+
return () => {
|
190 |
+
if (!files) return
|
191 |
+
files.forEach((file) => {
|
192 |
+
if (isFileWithPreview(file)) {
|
193 |
+
URL.revokeObjectURL(file.preview)
|
194 |
+
}
|
195 |
+
})
|
196 |
+
}
|
197 |
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
198 |
+
}, [])
|
199 |
+
|
200 |
+
const isDisabled = disabled || (files?.length ?? 0) >= maxFileCount
|
201 |
+
|
202 |
+
return (
|
203 |
+
<div className="relative flex flex-col gap-6 overflow-hidden">
|
204 |
+
<Dropzone
|
205 |
+
onDrop={onDrop}
|
206 |
+
accept={accept}
|
207 |
+
maxSize={maxSize}
|
208 |
+
maxFiles={maxFileCount}
|
209 |
+
multiple={maxFileCount > 1 || multiple}
|
210 |
+
disabled={isDisabled}
|
211 |
+
>
|
212 |
+
{({ getRootProps, getInputProps, isDragActive }) => (
|
213 |
+
<div
|
214 |
+
{...getRootProps()}
|
215 |
+
className={cn(
|
216 |
+
'group border-muted-foreground/25 hover:bg-muted/25 relative grid h-52 w-full cursor-pointer place-items-center rounded-lg border-2 border-dashed px-5 py-2.5 text-center transition',
|
217 |
+
'ring-offset-background focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
|
218 |
+
isDragActive && 'border-muted-foreground/50',
|
219 |
+
isDisabled && 'pointer-events-none opacity-60',
|
220 |
+
className
|
221 |
+
)}
|
222 |
+
{...dropzoneProps}
|
223 |
+
>
|
224 |
+
<input {...getInputProps()} />
|
225 |
+
{isDragActive ? (
|
226 |
+
<div className="flex flex-col items-center justify-center gap-4 sm:px-5">
|
227 |
+
<div className="rounded-full border border-dashed p-3">
|
228 |
+
<Upload className="text-muted-foreground size-7" aria-hidden="true" />
|
229 |
+
</div>
|
230 |
+
<p className="text-muted-foreground font-medium">Drop the files here</p>
|
231 |
+
</div>
|
232 |
+
) : (
|
233 |
+
<div className="flex flex-col items-center justify-center gap-4 sm:px-5">
|
234 |
+
<div className="rounded-full border border-dashed p-3">
|
235 |
+
<Upload className="text-muted-foreground size-7" aria-hidden="true" />
|
236 |
+
</div>
|
237 |
+
<div className="flex flex-col gap-px">
|
238 |
+
<p className="text-muted-foreground font-medium">
|
239 |
+
Drag and drop files here, or click to select files
|
240 |
+
</p>
|
241 |
+
{description ? (
|
242 |
+
<p className="text-muted-foreground/70 text-sm">{description}</p>
|
243 |
+
) : (
|
244 |
+
<p className="text-muted-foreground/70 text-sm">
|
245 |
+
You can upload
|
246 |
+
{maxFileCount > 1
|
247 |
+
? ` ${maxFileCount === Infinity ? 'multiple' : maxFileCount}
|
248 |
+
files (up to ${formatBytes(maxSize)} each)`
|
249 |
+
: ` a file with ${formatBytes(maxSize)}`}
|
250 |
+
Supported formats: TXT, MD, DOC, PDF, PPTX
|
251 |
+
</p>
|
252 |
+
)}
|
253 |
+
</div>
|
254 |
+
</div>
|
255 |
+
)}
|
256 |
+
</div>
|
257 |
+
)}
|
258 |
+
</Dropzone>
|
259 |
+
{files?.length ? (
|
260 |
+
<ScrollArea className="h-fit w-full px-3">
|
261 |
+
<div className="flex max-h-48 flex-col gap-4">
|
262 |
+
{files?.map((file, index) => (
|
263 |
+
<FileCard
|
264 |
+
key={index}
|
265 |
+
file={file}
|
266 |
+
onRemove={() => onRemove(index)}
|
267 |
+
progress={progresses?.[file.name]}
|
268 |
+
/>
|
269 |
+
))}
|
270 |
+
</div>
|
271 |
+
</ScrollArea>
|
272 |
+
) : null}
|
273 |
+
</div>
|
274 |
+
)
|
275 |
+
}
|
276 |
+
|
277 |
+
interface FileCardProps {
|
278 |
+
file: File
|
279 |
+
onRemove: () => void
|
280 |
+
progress?: number
|
281 |
+
}
|
282 |
+
|
283 |
+
function FileCard({ file, progress, onRemove }: FileCardProps) {
|
284 |
+
return (
|
285 |
+
<div className="relative flex items-center gap-2.5">
|
286 |
+
<div className="flex flex-1 gap-2.5">
|
287 |
+
{isFileWithPreview(file) ? <FilePreview file={file} /> : null}
|
288 |
+
<div className="flex w-full flex-col gap-2">
|
289 |
+
<div className="flex flex-col gap-px">
|
290 |
+
<p className="text-foreground/80 line-clamp-1 text-sm font-medium">{file.name}</p>
|
291 |
+
<p className="text-muted-foreground text-xs">{formatBytes(file.size)}</p>
|
292 |
+
</div>
|
293 |
+
{progress ? <Progress value={progress} /> : null}
|
294 |
+
</div>
|
295 |
+
</div>
|
296 |
+
<div className="flex items-center gap-2">
|
297 |
+
<Button type="button" variant="outline" size="icon" className="size-7" onClick={onRemove}>
|
298 |
+
<X className="size-4" aria-hidden="true" />
|
299 |
+
<span className="sr-only">Remove file</span>
|
300 |
+
</Button>
|
301 |
+
</div>
|
302 |
+
</div>
|
303 |
+
)
|
304 |
+
}
|
305 |
+
|
306 |
+
function isFileWithPreview(file: File): file is File & { preview: string } {
|
307 |
+
return 'preview' in file && typeof file.preview === 'string'
|
308 |
+
}
|
309 |
+
|
310 |
+
interface FilePreviewProps {
|
311 |
+
file: File & { preview: string }
|
312 |
+
}
|
313 |
+
|
314 |
+
function FilePreview({ file }: FilePreviewProps) {
|
315 |
+
if (file.type.startsWith('image/')) {
|
316 |
+
return <div className="aspect-square shrink-0 rounded-md object-cover" />
|
317 |
+
}
|
318 |
+
|
319 |
+
return <FileText className="text-muted-foreground size-10" aria-hidden="true" />
|
320 |
+
}
|
321 |
+
|
322 |
+
export default FileUploader
|
lightrag_webui/src/components/ui/Progress.tsx
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import * as ProgressPrimitive from '@radix-ui/react-progress'
|
3 |
+
|
4 |
+
import { cn } from '@/lib/utils'
|
5 |
+
|
6 |
+
const Progress = React.forwardRef<
|
7 |
+
React.ComponentRef<typeof ProgressPrimitive.Root>,
|
8 |
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
9 |
+
>(({ className, value, ...props }, ref) => (
|
10 |
+
<ProgressPrimitive.Root
|
11 |
+
ref={ref}
|
12 |
+
className={cn('bg-secondary relative h-4 w-full overflow-hidden rounded-full', className)}
|
13 |
+
{...props}
|
14 |
+
>
|
15 |
+
<ProgressPrimitive.Indicator
|
16 |
+
className="bg-primary h-full w-full flex-1 transition-all"
|
17 |
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
18 |
+
/>
|
19 |
+
</ProgressPrimitive.Root>
|
20 |
+
))
|
21 |
+
Progress.displayName = ProgressPrimitive.Root.displayName
|
22 |
+
|
23 |
+
export default Progress
|
lightrag_webui/src/components/ui/ScrollArea.tsx
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
|
3 |
+
|
4 |
+
import { cn } from '@/lib/utils'
|
5 |
+
|
6 |
+
const ScrollArea = React.forwardRef<
|
7 |
+
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
|
8 |
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
9 |
+
>(({ className, children, ...props }, ref) => (
|
10 |
+
<ScrollAreaPrimitive.Root
|
11 |
+
ref={ref}
|
12 |
+
className={cn('relative overflow-hidden', className)}
|
13 |
+
{...props}
|
14 |
+
>
|
15 |
+
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
16 |
+
{children}
|
17 |
+
</ScrollAreaPrimitive.Viewport>
|
18 |
+
<ScrollBar />
|
19 |
+
<ScrollAreaPrimitive.Corner />
|
20 |
+
</ScrollAreaPrimitive.Root>
|
21 |
+
))
|
22 |
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
23 |
+
|
24 |
+
const ScrollBar = React.forwardRef<
|
25 |
+
React.ComponentRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
26 |
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
27 |
+
>(({ className, orientation = 'vertical', ...props }, ref) => (
|
28 |
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
29 |
+
ref={ref}
|
30 |
+
orientation={orientation}
|
31 |
+
className={cn(
|
32 |
+
'flex touch-none transition-colors select-none',
|
33 |
+
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
34 |
+
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
35 |
+
className
|
36 |
+
)}
|
37 |
+
{...props}
|
38 |
+
>
|
39 |
+
<ScrollAreaPrimitive.ScrollAreaThumb className="bg-border relative flex-1 rounded-full" />
|
40 |
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
41 |
+
))
|
42 |
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
43 |
+
|
44 |
+
export { ScrollArea, ScrollBar }
|
lightrag_webui/src/components/ui/Table.tsx
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
|
3 |
+
import { cn } from '@/lib/utils'
|
4 |
+
|
5 |
+
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
|
6 |
+
({ className, ...props }, ref) => (
|
7 |
+
<div className="relative w-full overflow-auto">
|
8 |
+
<table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} />
|
9 |
+
</div>
|
10 |
+
)
|
11 |
+
)
|
12 |
+
Table.displayName = 'Table'
|
13 |
+
|
14 |
+
const TableHeader = React.forwardRef<
|
15 |
+
HTMLTableSectionElement,
|
16 |
+
React.HTMLAttributes<HTMLTableSectionElement>
|
17 |
+
>(({ className, ...props }, ref) => (
|
18 |
+
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
19 |
+
))
|
20 |
+
TableHeader.displayName = 'TableHeader'
|
21 |
+
|
22 |
+
const TableBody = React.forwardRef<
|
23 |
+
HTMLTableSectionElement,
|
24 |
+
React.HTMLAttributes<HTMLTableSectionElement>
|
25 |
+
>(({ className, ...props }, ref) => (
|
26 |
+
<tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
|
27 |
+
))
|
28 |
+
TableBody.displayName = 'TableBody'
|
29 |
+
|
30 |
+
const TableFooter = React.forwardRef<
|
31 |
+
HTMLTableSectionElement,
|
32 |
+
React.HTMLAttributes<HTMLTableSectionElement>
|
33 |
+
>(({ className, ...props }, ref) => (
|
34 |
+
<tfoot
|
35 |
+
ref={ref}
|
36 |
+
className={cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', className)}
|
37 |
+
{...props}
|
38 |
+
/>
|
39 |
+
))
|
40 |
+
TableFooter.displayName = 'TableFooter'
|
41 |
+
|
42 |
+
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
|
43 |
+
({ className, ...props }, ref) => (
|
44 |
+
<tr
|
45 |
+
ref={ref}
|
46 |
+
className={cn(
|
47 |
+
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
48 |
+
className
|
49 |
+
)}
|
50 |
+
{...props}
|
51 |
+
/>
|
52 |
+
)
|
53 |
+
)
|
54 |
+
TableRow.displayName = 'TableRow'
|
55 |
+
|
56 |
+
const TableHead = React.forwardRef<
|
57 |
+
HTMLTableCellElement,
|
58 |
+
React.ThHTMLAttributes<HTMLTableCellElement>
|
59 |
+
>(({ className, ...props }, ref) => (
|
60 |
+
<th
|
61 |
+
ref={ref}
|
62 |
+
className={cn(
|
63 |
+
'text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
64 |
+
className
|
65 |
+
)}
|
66 |
+
{...props}
|
67 |
+
/>
|
68 |
+
))
|
69 |
+
TableHead.displayName = 'TableHead'
|
70 |
+
|
71 |
+
const TableCell = React.forwardRef<
|
72 |
+
HTMLTableCellElement,
|
73 |
+
React.TdHTMLAttributes<HTMLTableCellElement>
|
74 |
+
>(({ className, ...props }, ref) => (
|
75 |
+
<td
|
76 |
+
ref={ref}
|
77 |
+
className={cn(
|
78 |
+
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
79 |
+
className
|
80 |
+
)}
|
81 |
+
{...props}
|
82 |
+
/>
|
83 |
+
))
|
84 |
+
TableCell.displayName = 'TableCell'
|
85 |
+
|
86 |
+
const TableCaption = React.forwardRef<
|
87 |
+
HTMLTableCaptionElement,
|
88 |
+
React.HTMLAttributes<HTMLTableCaptionElement>
|
89 |
+
>(({ className, ...props }, ref) => (
|
90 |
+
<caption ref={ref} className={cn('text-muted-foreground mt-4 text-sm', className)} {...props} />
|
91 |
+
))
|
92 |
+
TableCaption.displayName = 'TableCaption'
|
93 |
+
|
94 |
+
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }
|
lightrag_webui/src/components/ui/Tabs.tsx
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
3 |
+
|
4 |
+
import { cn } from '@/lib/utils'
|
5 |
+
|
6 |
+
const Tabs = TabsPrimitive.Root
|
7 |
+
|
8 |
+
const TabsList = React.forwardRef<
|
9 |
+
React.ComponentRef<typeof TabsPrimitive.List>,
|
10 |
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
11 |
+
>(({ className, ...props }, ref) => (
|
12 |
+
<TabsPrimitive.List
|
13 |
+
ref={ref}
|
14 |
+
className={cn(
|
15 |
+
'bg-muted text-muted-foreground inline-flex h-10 items-center justify-center rounded-md p-1',
|
16 |
+
className
|
17 |
+
)}
|
18 |
+
{...props}
|
19 |
+
/>
|
20 |
+
))
|
21 |
+
TabsList.displayName = TabsPrimitive.List.displayName
|
22 |
+
|
23 |
+
const TabsTrigger = React.forwardRef<
|
24 |
+
React.ComponentRef<typeof TabsPrimitive.Trigger>,
|
25 |
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
26 |
+
>(({ className, ...props }, ref) => (
|
27 |
+
<TabsPrimitive.Trigger
|
28 |
+
ref={ref}
|
29 |
+
className={cn(
|
30 |
+
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-all focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm',
|
31 |
+
className
|
32 |
+
)}
|
33 |
+
{...props}
|
34 |
+
/>
|
35 |
+
))
|
36 |
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
37 |
+
|
38 |
+
const TabsContent = React.forwardRef<
|
39 |
+
React.ComponentRef<typeof TabsPrimitive.Content>,
|
40 |
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
41 |
+
>(({ className, ...props }, ref) => (
|
42 |
+
<TabsPrimitive.Content
|
43 |
+
ref={ref}
|
44 |
+
className={cn(
|
45 |
+
'ring-offset-background focus-visible:ring-ring mt-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
|
46 |
+
className
|
47 |
+
)}
|
48 |
+
{...props}
|
49 |
+
/>
|
50 |
+
))
|
51 |
+
TabsContent.displayName = TabsPrimitive.Content.displayName
|
52 |
+
|
53 |
+
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
lightrag_webui/src/components/ui/Tooltip.tsx
CHANGED
@@ -16,7 +16,7 @@ const TooltipContent = React.forwardRef<
|
|
16 |
ref={ref}
|
17 |
sideOffset={sideOffset}
|
18 |
className={cn(
|
19 |
-
'bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md border px-3 py-1.5 text-sm shadow-md',
|
20 |
className
|
21 |
)}
|
22 |
{...props}
|
|
|
16 |
ref={ref}
|
17 |
sideOffset={sideOffset}
|
18 |
className={cn(
|
19 |
+
'bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 mx-1 overflow-hidden rounded-md border px-3 py-1.5 text-sm shadow-md',
|
20 |
className
|
21 |
)}
|
22 |
{...props}
|
lightrag_webui/src/features/DocumentManager.tsx
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useEffect, useCallback } from 'react'
|
2 |
+
import Button from '@/components/ui/Button'
|
3 |
+
import {
|
4 |
+
Table,
|
5 |
+
TableBody,
|
6 |
+
TableCell,
|
7 |
+
TableHead,
|
8 |
+
TableHeader,
|
9 |
+
TableRow
|
10 |
+
} from '@/components/ui/Table'
|
11 |
+
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/Card'
|
12 |
+
import Progress from '@/components/ui/Progress'
|
13 |
+
import EmptyCard from '@/components/ui/EmptyCard'
|
14 |
+
import UploadDocumentsDialog from '@/components/document/UploadDocumentsDialog'
|
15 |
+
import ClearDocumentsDialog from '@/components/document/ClearDocumentsDialog'
|
16 |
+
|
17 |
+
import {
|
18 |
+
getDocuments,
|
19 |
+
getDocumentsScanProgress,
|
20 |
+
scanNewDocuments,
|
21 |
+
LightragDocumentsScanProgress
|
22 |
+
} from '@/api/lightrag'
|
23 |
+
import { errorMessage } from '@/lib/utils'
|
24 |
+
import { toast } from 'sonner'
|
25 |
+
import { useBackendState } from '@/stores/state'
|
26 |
+
|
27 |
+
import { RefreshCwIcon, TrashIcon } from 'lucide-react'
|
28 |
+
|
29 |
+
// type DocumentStatus = 'indexed' | 'pending' | 'indexing' | 'error'
|
30 |
+
|
31 |
+
export default function DocumentManager() {
|
32 |
+
const health = useBackendState.use.health()
|
33 |
+
const [files, setFiles] = useState<string[]>([])
|
34 |
+
const [indexedFiles, setIndexedFiles] = useState<string[]>([])
|
35 |
+
const [scanProgress, setScanProgress] = useState<LightragDocumentsScanProgress | null>(null)
|
36 |
+
|
37 |
+
const fetchDocuments = useCallback(async () => {
|
38 |
+
try {
|
39 |
+
const docs = await getDocuments()
|
40 |
+
setFiles(docs)
|
41 |
+
} catch (err) {
|
42 |
+
toast.error('Failed to load documents\n' + errorMessage(err))
|
43 |
+
}
|
44 |
+
}, [setFiles])
|
45 |
+
|
46 |
+
useEffect(() => {
|
47 |
+
fetchDocuments()
|
48 |
+
}, [])
|
49 |
+
|
50 |
+
const scanDocuments = useCallback(async () => {
|
51 |
+
try {
|
52 |
+
const { status } = await scanNewDocuments()
|
53 |
+
toast.message(status)
|
54 |
+
} catch (err) {
|
55 |
+
toast.error('Failed to load documents\n' + errorMessage(err))
|
56 |
+
}
|
57 |
+
}, [setFiles])
|
58 |
+
|
59 |
+
useEffect(() => {
|
60 |
+
const interval = setInterval(async () => {
|
61 |
+
try {
|
62 |
+
if (!health) return
|
63 |
+
const progress = await getDocumentsScanProgress()
|
64 |
+
setScanProgress((pre) => {
|
65 |
+
if (pre?.is_scanning === progress.is_scanning && progress.is_scanning === false) {
|
66 |
+
return pre
|
67 |
+
}
|
68 |
+
return progress
|
69 |
+
})
|
70 |
+
console.log(progress)
|
71 |
+
} catch (err) {
|
72 |
+
toast.error('Failed to get scan progress\n' + errorMessage(err))
|
73 |
+
}
|
74 |
+
}, 2000)
|
75 |
+
return () => clearInterval(interval)
|
76 |
+
}, [health])
|
77 |
+
|
78 |
+
const handleDelete = async (fileName: string) => {
|
79 |
+
console.log(`deleting ${fileName}`)
|
80 |
+
}
|
81 |
+
|
82 |
+
return (
|
83 |
+
<Card className="!size-full !rounded-none !border-none">
|
84 |
+
<CardHeader>
|
85 |
+
<CardTitle className="text-lg">Document Management</CardTitle>
|
86 |
+
</CardHeader>
|
87 |
+
<CardContent className="space-y-4">
|
88 |
+
<div className="flex gap-2">
|
89 |
+
<Button
|
90 |
+
variant="outline"
|
91 |
+
size="icon"
|
92 |
+
tooltip="Scan Documents"
|
93 |
+
onClick={scanDocuments}
|
94 |
+
side="bottom"
|
95 |
+
>
|
96 |
+
<RefreshCwIcon />
|
97 |
+
</Button>
|
98 |
+
<div className="flex-1" />
|
99 |
+
<ClearDocumentsDialog />
|
100 |
+
<UploadDocumentsDialog />
|
101 |
+
</div>
|
102 |
+
|
103 |
+
{scanProgress?.is_scanning && (
|
104 |
+
<div className="space-y-2">
|
105 |
+
<div className="flex justify-between text-sm">
|
106 |
+
<span>Indexing {scanProgress.current_file}</span>
|
107 |
+
<span>{scanProgress.progress}%</span>
|
108 |
+
</div>
|
109 |
+
<Progress value={scanProgress.progress} />
|
110 |
+
</div>
|
111 |
+
)}
|
112 |
+
|
113 |
+
<Card>
|
114 |
+
<CardHeader>
|
115 |
+
<CardTitle>Uploaded documents</CardTitle>
|
116 |
+
<CardDescription>view the uploaded documents here</CardDescription>
|
117 |
+
</CardHeader>
|
118 |
+
|
119 |
+
<CardContent>
|
120 |
+
{files.length == 0 && (
|
121 |
+
<EmptyCard
|
122 |
+
title="No documents uploades"
|
123 |
+
description="upload documents to see them here"
|
124 |
+
/>
|
125 |
+
)}
|
126 |
+
{files.length > 0 && (
|
127 |
+
<Table>
|
128 |
+
<TableHeader>
|
129 |
+
<TableRow>
|
130 |
+
<TableHead>Filename</TableHead>
|
131 |
+
<TableHead>Status</TableHead>
|
132 |
+
<TableHead>Actions</TableHead>
|
133 |
+
</TableRow>
|
134 |
+
</TableHeader>
|
135 |
+
<TableBody>
|
136 |
+
{files.map((file) => (
|
137 |
+
<TableRow key={file}>
|
138 |
+
<TableCell>{file}</TableCell>
|
139 |
+
<TableCell>
|
140 |
+
{indexedFiles.includes(file) ? (
|
141 |
+
<span className="text-green-600">Indexed</span>
|
142 |
+
) : (
|
143 |
+
<span className="text-yellow-600">Pending</span>
|
144 |
+
)}
|
145 |
+
</TableCell>
|
146 |
+
<TableCell>
|
147 |
+
<Button
|
148 |
+
variant="ghost"
|
149 |
+
size="sm"
|
150 |
+
onClick={() => handleDelete(file)}
|
151 |
+
// disabled={isUploading}
|
152 |
+
>
|
153 |
+
<TrashIcon />
|
154 |
+
</Button>
|
155 |
+
</TableCell>
|
156 |
+
</TableRow>
|
157 |
+
))}
|
158 |
+
</TableBody>
|
159 |
+
</Table>
|
160 |
+
)}
|
161 |
+
</CardContent>
|
162 |
+
</Card>
|
163 |
+
</CardContent>
|
164 |
+
</Card>
|
165 |
+
)
|
166 |
+
}
|
lightrag_webui/src/{GraphViewer.tsx → features/GraphViewer.tsx}
RENAMED
@@ -10,7 +10,7 @@ import EdgeCurveProgram, { EdgeCurvedArrowProgram } from '@sigma/edge-curve'
|
|
10 |
import FocusOnNode from '@/components/FocusOnNode'
|
11 |
import LayoutsControl from '@/components/LayoutsControl'
|
12 |
import GraphControl from '@/components/GraphControl'
|
13 |
-
import ThemeToggle from '@/components/ThemeToggle'
|
14 |
import ZoomControl from '@/components/ZoomControl'
|
15 |
import FullScreenControl from '@/components/FullScreenControl'
|
16 |
import Settings from '@/components/Settings'
|
@@ -166,7 +166,7 @@ const GraphViewer = () => {
|
|
166 |
<ZoomControl />
|
167 |
<LayoutsControl />
|
168 |
<FullScreenControl />
|
169 |
-
<ThemeToggle />
|
170 |
</div>
|
171 |
|
172 |
{showPropertyPanel && (
|
|
|
10 |
import FocusOnNode from '@/components/FocusOnNode'
|
11 |
import LayoutsControl from '@/components/LayoutsControl'
|
12 |
import GraphControl from '@/components/GraphControl'
|
13 |
+
// import ThemeToggle from '@/components/ThemeToggle'
|
14 |
import ZoomControl from '@/components/ZoomControl'
|
15 |
import FullScreenControl from '@/components/FullScreenControl'
|
16 |
import Settings from '@/components/Settings'
|
|
|
166 |
<ZoomControl />
|
167 |
<LayoutsControl />
|
168 |
<FullScreenControl />
|
169 |
+
{/* <ThemeToggle /> */}
|
170 |
</div>
|
171 |
|
172 |
{showPropertyPanel && (
|
lightrag_webui/src/features/SiteHeader.tsx
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Button from '@/components/ui/Button'
|
2 |
+
import { SiteInfo } from '@/lib/constants'
|
3 |
+
import ThemeToggle from '@/components/ThemeToggle'
|
4 |
+
import { TabsList, TabsTrigger } from '@/components/ui/Tabs'
|
5 |
+
|
6 |
+
import { ZapIcon, GithubIcon } from 'lucide-react'
|
7 |
+
|
8 |
+
export default function SiteHeader() {
|
9 |
+
return (
|
10 |
+
<header className="border-border/40 bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 flex h-10 w-full border-b px-4 backdrop-blur">
|
11 |
+
<a href="/" className="mr-6 flex items-center gap-2">
|
12 |
+
<ZapIcon className="size-4 text-teal-400" aria-hidden="true" />
|
13 |
+
<span className="font-bold md:inline-block">{SiteInfo.name}</span>
|
14 |
+
</a>
|
15 |
+
|
16 |
+
<div className="flex h-10 flex-1 justify-center">
|
17 |
+
<div className="flex h-8 self-center">
|
18 |
+
<TabsList className="h-full gap-2">
|
19 |
+
<TabsTrigger
|
20 |
+
value="documents"
|
21 |
+
className="hover:bg-background/60 cursor-pointer px-2 py-1 transition-all"
|
22 |
+
>
|
23 |
+
Documents
|
24 |
+
</TabsTrigger>
|
25 |
+
<TabsTrigger
|
26 |
+
value="knowledge-graph"
|
27 |
+
className="hover:bg-background/60 cursor-pointer px-2 py-1 transition-all"
|
28 |
+
>
|
29 |
+
Knowledge Graph
|
30 |
+
</TabsTrigger>
|
31 |
+
{/* <TabsTrigger
|
32 |
+
value="settings"
|
33 |
+
className="hover:bg-background/60 cursor-pointer px-2 py-1 transition-all"
|
34 |
+
>
|
35 |
+
Settings
|
36 |
+
</TabsTrigger> */}
|
37 |
+
</TabsList>
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
|
41 |
+
<nav className="flex items-center">
|
42 |
+
<Button variant="ghost" size="icon">
|
43 |
+
<a href={SiteInfo.github} target="_blank" rel="noopener noreferrer">
|
44 |
+
<GithubIcon className="size-4" aria-hidden="true" />
|
45 |
+
</a>
|
46 |
+
</Button>
|
47 |
+
<ThemeToggle />
|
48 |
+
</nav>
|
49 |
+
</header>
|
50 |
+
)
|
51 |
+
}
|
lightrag_webui/src/hooks/useLightragGraph.tsx
CHANGED
@@ -24,7 +24,7 @@ const validateGraph = (graph: RawGraph) => {
|
|
24 |
}
|
25 |
|
26 |
for (const edge of graph.edges) {
|
27 |
-
if (!edge.id || !edge.source || !edge.target
|
28 |
return false
|
29 |
}
|
30 |
}
|
@@ -88,6 +88,14 @@ const fetchGraph = async (label: string) => {
|
|
88 |
if (source !== undefined && source !== undefined) {
|
89 |
const sourceNode = rawData.nodes[source]
|
90 |
const targetNode = rawData.nodes[target]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
sourceNode.degree += 1
|
92 |
targetNode.degree += 1
|
93 |
}
|
@@ -146,7 +154,7 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
|
|
146 |
|
147 |
for (const rawEdge of rawGraph?.edges ?? []) {
|
148 |
rawEdge.dynamicId = graph.addDirectedEdge(rawEdge.source, rawEdge.target, {
|
149 |
-
label: rawEdge.type
|
150 |
})
|
151 |
}
|
152 |
|
|
|
24 |
}
|
25 |
|
26 |
for (const edge of graph.edges) {
|
27 |
+
if (!edge.id || !edge.source || !edge.target) {
|
28 |
return false
|
29 |
}
|
30 |
}
|
|
|
88 |
if (source !== undefined && source !== undefined) {
|
89 |
const sourceNode = rawData.nodes[source]
|
90 |
const targetNode = rawData.nodes[target]
|
91 |
+
if (!sourceNode) {
|
92 |
+
console.error(`Source node ${edge.source} is undefined`)
|
93 |
+
continue
|
94 |
+
}
|
95 |
+
if (!targetNode) {
|
96 |
+
console.error(`Target node ${edge.target} is undefined`)
|
97 |
+
continue
|
98 |
+
}
|
99 |
sourceNode.degree += 1
|
100 |
targetNode.degree += 1
|
101 |
}
|
|
|
154 |
|
155 |
for (const rawEdge of rawGraph?.edges ?? []) {
|
156 |
rawEdge.dynamicId = graph.addDirectedEdge(rawEdge.source, rawEdge.target, {
|
157 |
+
label: rawEdge.type || undefined
|
158 |
})
|
159 |
}
|
160 |
|
lightrag_webui/src/index.css
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
@import 'tailwindcss';
|
2 |
|
3 |
@plugin 'tailwindcss-animate';
|
|
|
4 |
|
5 |
@custom-variant dark (&:is(.dark *));
|
6 |
|
@@ -142,3 +143,27 @@
|
|
142 |
@apply bg-background text-foreground;
|
143 |
}
|
144 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
@import 'tailwindcss';
|
2 |
|
3 |
@plugin 'tailwindcss-animate';
|
4 |
+
@plugin 'tailwind-scrollbar';
|
5 |
|
6 |
@custom-variant dark (&:is(.dark *));
|
7 |
|
|
|
143 |
@apply bg-background text-foreground;
|
144 |
}
|
145 |
}
|
146 |
+
|
147 |
+
::-webkit-scrollbar {
|
148 |
+
width: 10px;
|
149 |
+
height: 10px;
|
150 |
+
}
|
151 |
+
|
152 |
+
::-webkit-scrollbar-thumb {
|
153 |
+
background-color: hsl(0 0% 80%);
|
154 |
+
border-radius: 5px;
|
155 |
+
}
|
156 |
+
|
157 |
+
::-webkit-scrollbar-track {
|
158 |
+
background-color: hsl(0 0% 95%);
|
159 |
+
}
|
160 |
+
|
161 |
+
.dark {
|
162 |
+
::-webkit-scrollbar-thumb {
|
163 |
+
background-color: hsl(0 0% 90%);
|
164 |
+
}
|
165 |
+
|
166 |
+
::-webkit-scrollbar-track {
|
167 |
+
background-color: hsl(0 0% 0%);
|
168 |
+
}
|
169 |
+
}
|
lightrag_webui/src/lib/constants.ts
CHANGED
@@ -23,3 +23,18 @@ export const maxNodeSize = 20
|
|
23 |
export const healthCheckInterval = 15 // seconds
|
24 |
|
25 |
export const defaultQueryLabel = '*'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
export const healthCheckInterval = 15 // seconds
|
24 |
|
25 |
export const defaultQueryLabel = '*'
|
26 |
+
|
27 |
+
// reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/MIME_types/Common_types
|
28 |
+
export const supportedFileTypes = {
|
29 |
+
'text/plain': ['.txt', '.md'],
|
30 |
+
'application/pdf': ['.pdf'],
|
31 |
+
'application/msword': ['.doc'],
|
32 |
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
|
33 |
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['.pptx']
|
34 |
+
}
|
35 |
+
|
36 |
+
export const SiteInfo = {
|
37 |
+
name: 'LightRAG',
|
38 |
+
home: '/',
|
39 |
+
github: 'https://github.com/HKUDS/LightRAG'
|
40 |
+
}
|
lightrag_webui/src/stores/graph.ts
CHANGED
@@ -19,7 +19,7 @@ export type RawEdgeType = {
|
|
19 |
id: string
|
20 |
source: string
|
21 |
target: string
|
22 |
-
type
|
23 |
properties: Record<string, any>
|
24 |
|
25 |
dynamicId: string
|
|
|
19 |
id: string
|
20 |
source: string
|
21 |
target: string
|
22 |
+
type?: string
|
23 |
properties: Record<string, any>
|
24 |
|
25 |
dynamicId: string
|