ArnoChen commited on
Commit
0ef8cba
Β·
1 Parent(s): a2251a6

implement API key alert

Browse files
Files changed (23) hide show
  1. lightrag_webui/README.md +1 -1
  2. lightrag_webui/bun.lock +3 -0
  3. lightrag_webui/package.json +1 -0
  4. lightrag_webui/src/App.tsx +15 -3
  5. lightrag_webui/src/components/ApiKeyAlert.tsx +79 -0
  6. lightrag_webui/src/components/{FocusOnNode.tsx β†’ graph/FocusOnNode.tsx} +0 -0
  7. lightrag_webui/src/components/{FullScreenControl.tsx β†’ graph/FullScreenControl.tsx} +0 -0
  8. lightrag_webui/src/components/{GraphControl.tsx β†’ graph/GraphControl.tsx} +0 -0
  9. lightrag_webui/src/components/{GraphLabels.tsx β†’ graph/GraphLabels.tsx} +0 -0
  10. lightrag_webui/src/components/{GraphSearch.tsx β†’ graph/GraphSearch.tsx} +0 -0
  11. lightrag_webui/src/components/{LayoutsControl.tsx β†’ graph/LayoutsControl.tsx} +0 -0
  12. lightrag_webui/src/components/{PropertiesView.tsx β†’ graph/PropertiesView.tsx} +0 -0
  13. lightrag_webui/src/components/{Settings.tsx β†’ graph/Settings.tsx} +1 -1
  14. lightrag_webui/src/components/{StatusCard.tsx β†’ graph/StatusCard.tsx} +0 -0
  15. lightrag_webui/src/components/{StatusIndicator.tsx β†’ graph/StatusIndicator.tsx} +1 -1
  16. lightrag_webui/src/components/{ThemeProvider.tsx β†’ graph/ThemeProvider.tsx} +0 -0
  17. lightrag_webui/src/components/{ThemeToggle.tsx β†’ graph/ThemeToggle.tsx} +0 -0
  18. lightrag_webui/src/components/{ZoomControl.tsx β†’ graph/ZoomControl.tsx} +0 -0
  19. lightrag_webui/src/components/ui/AlertDialog.tsx +115 -0
  20. lightrag_webui/src/components/ui/Button.tsx +2 -1
  21. lightrag_webui/src/features/GraphViewer.tsx +9 -9
  22. lightrag_webui/src/features/SiteHeader.tsx +1 -1
  23. lightrag_webui/src/hooks/useTheme.tsx +1 -1
lightrag_webui/README.md CHANGED
@@ -21,7 +21,7 @@ LightRAG WebUI is a React-based web interface for interacting with the LightRAG
21
  Run the following command to build the project:
22
 
23
  ```bash
24
- bun run build
25
  ```
26
 
27
  This command will bundle the project and output the built files to the `lightrag/api/webui` directory.
 
21
  Run the following command to build the project:
22
 
23
  ```bash
24
+ bun run build --emptyOutDir
25
  ```
26
 
27
  This command will bundle the project and output the built files to the `lightrag/api/webui` directory.
lightrag_webui/bun.lock CHANGED
@@ -5,6 +5,7 @@
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",
@@ -220,6 +221,8 @@
220
 
221
  "@radix-ui/primitive": ["@radix-ui/[email protected]", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
222
 
 
 
223
  "@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=="],
224
 
225
  "@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=="],
 
5
  "name": "lightrag-webui",
6
  "dependencies": {
7
  "@faker-js/faker": "^9.5.0",
8
+ "@radix-ui/react-alert-dialog": "^1.1.6",
9
  "@radix-ui/react-checkbox": "^1.1.4",
10
  "@radix-ui/react-dialog": "^1.1.6",
11
  "@radix-ui/react-popover": "^1.1.6",
 
221
 
222
  "@radix-ui/primitive": ["@radix-ui/[email protected]", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
223
 
224
+ "@radix-ui/react-alert-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-dialog": "1.1.6", "@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-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ=="],
225
+
226
  "@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=="],
227
 
228
  "@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=="],
lightrag_webui/package.json CHANGED
@@ -11,6 +11,7 @@
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",
 
11
  },
12
  "dependencies": {
13
  "@faker-js/faker": "^9.5.0",
14
+ "@radix-ui/react-alert-dialog": "^1.1.6",
15
  "@radix-ui/react-checkbox": "^1.1.4",
16
  "@radix-ui/react-dialog": "^1.1.6",
17
  "@radix-ui/react-popover": "^1.1.6",
lightrag_webui/src/App.tsx CHANGED
@@ -1,13 +1,15 @@
1
  import { useState, useCallback } from 'react'
2
- import ThemeProvider from '@/components/ThemeProvider'
3
  import MessageAlert from '@/components/MessageAlert'
4
- import StatusIndicator from '@/components/StatusIndicator'
 
5
  import { healthCheckInterval } from '@/lib/constants'
6
  import { useBackendState } from '@/stores/state'
7
  import { useSettingsStore } from '@/stores/settings'
8
  import { useEffect } from 'react'
9
  import { Toaster } from 'sonner'
10
  import SiteHeader from '@/features/SiteHeader'
 
11
 
12
  import GraphViewer from '@/features/GraphViewer'
13
  import DocumentManager from '@/features/DocumentManager'
@@ -20,6 +22,7 @@ function App() {
20
  const message = useBackendState.use.message()
21
  const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
22
  const [currentTab] = useState(() => useSettingsStore.getState().currentTab)
 
23
 
24
  // Health check
25
  useEffect(() => {
@@ -39,6 +42,14 @@ function App() {
39
  []
40
  )
41
 
 
 
 
 
 
 
 
 
42
  return (
43
  <ThemeProvider>
44
  <main className="flex h-screen w-screen overflow-x-hidden">
@@ -64,7 +75,8 @@ function App() {
64
  </div>
65
  </Tabs>
66
  {enableHealthCheck && <StatusIndicator />}
67
- {message !== null && <MessageAlert />}
 
68
  <Toaster />
69
  </main>
70
  </ThemeProvider>
 
1
  import { useState, useCallback } from 'react'
2
+ import ThemeProvider from '@/components/graph/ThemeProvider'
3
  import MessageAlert from '@/components/MessageAlert'
4
+ import ApiKeyAlert from '@/components/ApiKeyAlert'
5
+ import StatusIndicator from '@/components/graph/StatusIndicator'
6
  import { healthCheckInterval } from '@/lib/constants'
7
  import { useBackendState } from '@/stores/state'
8
  import { useSettingsStore } from '@/stores/settings'
9
  import { useEffect } from 'react'
10
  import { Toaster } from 'sonner'
11
  import SiteHeader from '@/features/SiteHeader'
12
+ import { InvalidApiKeyError, RequireApiKeError } from '@/api/lightrag'
13
 
14
  import GraphViewer from '@/features/GraphViewer'
15
  import DocumentManager from '@/features/DocumentManager'
 
22
  const message = useBackendState.use.message()
23
  const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
24
  const [currentTab] = useState(() => useSettingsStore.getState().currentTab)
25
+ const [apiKeyInvalid, setApiKeyInvalid] = useState(false)
26
 
27
  // Health check
28
  useEffect(() => {
 
42
  []
43
  )
44
 
45
+ useEffect(() => {
46
+ if (message) {
47
+ if (message.includes(InvalidApiKeyError) || message.includes(RequireApiKeError)) {
48
+ setApiKeyInvalid(true)
49
+ }
50
+ }
51
+ }, [message, setApiKeyInvalid])
52
+
53
  return (
54
  <ThemeProvider>
55
  <main className="flex h-screen w-screen overflow-x-hidden">
 
75
  </div>
76
  </Tabs>
77
  {enableHealthCheck && <StatusIndicator />}
78
+ {message !== null && !apiKeyInvalid && <MessageAlert />}
79
+ {apiKeyInvalid && <ApiKeyAlert />}
80
  <Toaster />
81
  </main>
82
  </ThemeProvider>
lightrag_webui/src/components/ApiKeyAlert.tsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useCallback, useEffect } from 'react'
2
+ import {
3
+ AlertDialog,
4
+ AlertDialogContent,
5
+ AlertDialogDescription,
6
+ AlertDialogHeader,
7
+ AlertDialogTitle,
8
+ AlertDialogTrigger
9
+ } from '@/components/ui/AlertDialog'
10
+ import Button from '@/components/ui/Button'
11
+ import Input from '@/components/ui/Input'
12
+ import { useSettingsStore } from '@/stores/settings'
13
+ import { useBackendState } from '@/stores/state'
14
+ import { InvalidApiKeyError, RequireApiKeError } from '@/api/lightrag'
15
+
16
+ import { toast } from 'sonner'
17
+
18
+ const ApiKeyAlert = () => {
19
+ const [opened, setOpened] = useState<boolean>(true)
20
+ const apiKey = useSettingsStore.use.apiKey()
21
+ const [tempApiKey, setTempApiKey] = useState<string>('')
22
+ const message = useBackendState.use.message()
23
+
24
+ useEffect(() => {
25
+ setTempApiKey(apiKey || '')
26
+ }, [apiKey, opened])
27
+
28
+ useEffect(() => {
29
+ if (message) {
30
+ if (message.includes(InvalidApiKeyError) || message.includes(RequireApiKeError)) {
31
+ setOpened(true)
32
+ }
33
+ }
34
+ }, [message, setOpened])
35
+
36
+ const setApiKey = useCallback(async () => {
37
+ useSettingsStore.setState({ apiKey: tempApiKey || null })
38
+ if (await useBackendState.getState().check()) {
39
+ setOpened(false)
40
+ return
41
+ }
42
+ toast.error('API Key is invalid')
43
+ }, [tempApiKey])
44
+
45
+ const handleTempApiKeyChange = useCallback(
46
+ (e: React.ChangeEvent<HTMLInputElement>) => {
47
+ setTempApiKey(e.target.value)
48
+ },
49
+ [setTempApiKey]
50
+ )
51
+
52
+ return (
53
+ <AlertDialog open={opened} onOpenChange={setOpened}>
54
+ <AlertDialogTrigger>Open</AlertDialogTrigger>
55
+ <AlertDialogContent>
56
+ <AlertDialogHeader>
57
+ <AlertDialogTitle>API Key is required</AlertDialogTitle>
58
+ <AlertDialogDescription>Please enter your API key</AlertDialogDescription>
59
+ </AlertDialogHeader>
60
+ <form className="flex gap-2" onSubmit={(e) => e.preventDefault()}>
61
+ <Input
62
+ type="password"
63
+ value={tempApiKey}
64
+ onChange={handleTempApiKeyChange}
65
+ placeholder="Enter your API key"
66
+ className="max-h-full w-full min-w-0"
67
+ autoComplete="off"
68
+ />
69
+
70
+ <Button onClick={setApiKey} variant="outline" size="sm">
71
+ Save
72
+ </Button>
73
+ </form>
74
+ </AlertDialogContent>
75
+ </AlertDialog>
76
+ )
77
+ }
78
+
79
+ export default ApiKeyAlert
lightrag_webui/src/components/{FocusOnNode.tsx β†’ graph/FocusOnNode.tsx} RENAMED
File without changes
lightrag_webui/src/components/{FullScreenControl.tsx β†’ graph/FullScreenControl.tsx} RENAMED
File without changes
lightrag_webui/src/components/{GraphControl.tsx β†’ graph/GraphControl.tsx} RENAMED
File without changes
lightrag_webui/src/components/{GraphLabels.tsx β†’ graph/GraphLabels.tsx} RENAMED
File without changes
lightrag_webui/src/components/{GraphSearch.tsx β†’ graph/GraphSearch.tsx} RENAMED
File without changes
lightrag_webui/src/components/{LayoutsControl.tsx β†’ graph/LayoutsControl.tsx} RENAMED
File without changes
lightrag_webui/src/components/{PropertiesView.tsx β†’ graph/PropertiesView.tsx} RENAMED
File without changes
lightrag_webui/src/components/{Settings.tsx β†’ graph/Settings.tsx} RENAMED
@@ -40,7 +40,7 @@ const LabeledCheckBox = ({
40
  */
41
  export default function Settings() {
42
  const [opened, setOpened] = useState<boolean>(false)
43
- const [tempApiKey, setTempApiKey] = useState<string>('') // η”¨δΊŽδΈ΄ζ—Άε­˜ε‚¨θΎ“ε…₯ηš„API Key
44
 
45
  const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
46
  const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
 
40
  */
41
  export default function Settings() {
42
  const [opened, setOpened] = useState<boolean>(false)
43
+ const [tempApiKey, setTempApiKey] = useState<string>('')
44
 
45
  const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
46
  const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
lightrag_webui/src/components/{StatusCard.tsx β†’ graph/StatusCard.tsx} RENAMED
File without changes
lightrag_webui/src/components/{StatusIndicator.tsx β†’ graph/StatusIndicator.tsx} RENAMED
@@ -2,7 +2,7 @@ import { cn } from '@/lib/utils'
2
  import { useBackendState } from '@/stores/state'
3
  import { useEffect, useState } from 'react'
4
  import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
5
- import StatusCard from '@/components/StatusCard'
6
 
7
  const StatusIndicator = () => {
8
  const health = useBackendState.use.health()
 
2
  import { useBackendState } from '@/stores/state'
3
  import { useEffect, useState } from 'react'
4
  import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
5
+ import StatusCard from '@/components/graph/StatusCard'
6
 
7
  const StatusIndicator = () => {
8
  const health = useBackendState.use.health()
lightrag_webui/src/components/{ThemeProvider.tsx β†’ graph/ThemeProvider.tsx} RENAMED
File without changes
lightrag_webui/src/components/{ThemeToggle.tsx β†’ graph/ThemeToggle.tsx} RENAMED
File without changes
lightrag_webui/src/components/{ZoomControl.tsx β†’ graph/ZoomControl.tsx} RENAMED
File without changes
lightrag_webui/src/components/ui/AlertDialog.tsx ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from 'react'
2
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
3
+
4
+ import { cn } from '@/lib/utils'
5
+ import { buttonVariants } from '@/components/ui/Button'
6
+
7
+ const AlertDialog = AlertDialogPrimitive.Root
8
+
9
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger
10
+
11
+ const AlertDialogPortal = AlertDialogPrimitive.Portal
12
+
13
+ const AlertDialogOverlay = React.forwardRef<
14
+ React.ComponentRef<typeof AlertDialogPrimitive.Overlay>,
15
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
16
+ >(({ className, ...props }, ref) => (
17
+ <AlertDialogPrimitive.Overlay
18
+ className={cn(
19
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
20
+ className
21
+ )}
22
+ {...props}
23
+ ref={ref}
24
+ />
25
+ ))
26
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
27
+
28
+ const AlertDialogContent = React.forwardRef<
29
+ React.ComponentRef<typeof AlertDialogPrimitive.Content>,
30
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
31
+ >(({ className, ...props }, ref) => (
32
+ <AlertDialogPortal>
33
+ <AlertDialogOverlay />
34
+ <AlertDialogPrimitive.Content
35
+ ref={ref}
36
+ className={cn(
37
+ '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',
38
+ className
39
+ )}
40
+ {...props}
41
+ />
42
+ </AlertDialogPortal>
43
+ ))
44
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
45
+
46
+ const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
47
+ <div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
48
+ )
49
+ AlertDialogHeader.displayName = 'AlertDialogHeader'
50
+
51
+ const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
52
+ <div
53
+ className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
54
+ {...props}
55
+ />
56
+ )
57
+ AlertDialogFooter.displayName = 'AlertDialogFooter'
58
+
59
+ const AlertDialogTitle = React.forwardRef<
60
+ React.ComponentRef<typeof AlertDialogPrimitive.Title>,
61
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
62
+ >(({ className, ...props }, ref) => (
63
+ <AlertDialogPrimitive.Title
64
+ ref={ref}
65
+ className={cn('text-lg font-semibold', className)}
66
+ {...props}
67
+ />
68
+ ))
69
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
70
+
71
+ const AlertDialogDescription = React.forwardRef<
72
+ React.ComponentRef<typeof AlertDialogPrimitive.Description>,
73
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
74
+ >(({ className, ...props }, ref) => (
75
+ <AlertDialogPrimitive.Description
76
+ ref={ref}
77
+ className={cn('text-muted-foreground text-sm', className)}
78
+ {...props}
79
+ />
80
+ ))
81
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName
82
+
83
+ const AlertDialogAction = React.forwardRef<
84
+ React.ComponentRef<typeof AlertDialogPrimitive.Action>,
85
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
86
+ >(({ className, ...props }, ref) => (
87
+ <AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
88
+ ))
89
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
90
+
91
+ const AlertDialogCancel = React.forwardRef<
92
+ React.ComponentRef<typeof AlertDialogPrimitive.Cancel>,
93
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
94
+ >(({ className, ...props }, ref) => (
95
+ <AlertDialogPrimitive.Cancel
96
+ ref={ref}
97
+ className={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
98
+ {...props}
99
+ />
100
+ ))
101
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
102
+
103
+ export {
104
+ AlertDialog,
105
+ AlertDialogPortal,
106
+ AlertDialogOverlay,
107
+ AlertDialogTrigger,
108
+ AlertDialogContent,
109
+ AlertDialogHeader,
110
+ AlertDialogFooter,
111
+ AlertDialogTitle,
112
+ AlertDialogDescription,
113
+ AlertDialogAction,
114
+ AlertDialogCancel
115
+ }
lightrag_webui/src/components/ui/Button.tsx CHANGED
@@ -4,7 +4,8 @@ import { cva, type VariantProps } from 'class-variance-authority'
4
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/Tooltip'
5
  import { cn } from '@/lib/utils'
6
 
7
- const buttonVariants = cva(
 
8
  'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
9
  {
10
  variants: {
 
4
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/Tooltip'
5
  import { cn } from '@/lib/utils'
6
 
7
+ // eslint-disable-next-line react-refresh/only-export-components
8
+ export const buttonVariants = cva(
9
  'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
10
  {
11
  variants: {
lightrag_webui/src/features/GraphViewer.tsx CHANGED
@@ -7,16 +7,16 @@ import { EdgeArrowProgram, NodePointProgram, NodeCircleProgram } from 'sigma/ren
7
  import { NodeBorderProgram } from '@sigma/node-border'
8
  import EdgeCurveProgram, { EdgeCurvedArrowProgram } from '@sigma/edge-curve'
9
 
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'
17
- import GraphSearch from '@/components/GraphSearch'
18
- import GraphLabels from '@/components/GraphLabels'
19
- import PropertiesView from '@/components/PropertiesView'
20
 
21
  import { useSettingsStore } from '@/stores/settings'
22
  import { useGraphStore } from '@/stores/graph'
 
7
  import { NodeBorderProgram } from '@sigma/node-border'
8
  import EdgeCurveProgram, { EdgeCurvedArrowProgram } from '@sigma/edge-curve'
9
 
10
+ import FocusOnNode from '@/components/graph/FocusOnNode'
11
+ import LayoutsControl from '@/components/graph/LayoutsControl'
12
+ import GraphControl from '@/components/graph/GraphControl'
13
  // import ThemeToggle from '@/components/ThemeToggle'
14
+ import ZoomControl from '@/components/graph/ZoomControl'
15
+ import FullScreenControl from '@/components/graph/FullScreenControl'
16
+ import Settings from '@/components/graph/Settings'
17
+ import GraphSearch from '@/components/graph/GraphSearch'
18
+ import GraphLabels from '@/components/graph/GraphLabels'
19
+ import PropertiesView from '@/components/graph/PropertiesView'
20
 
21
  import { useSettingsStore } from '@/stores/settings'
22
  import { useGraphStore } from '@/stores/graph'
lightrag_webui/src/features/SiteHeader.tsx CHANGED
@@ -1,6 +1,6 @@
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
  import { useSettingsStore } from '@/stores/settings'
6
  import { cn } from '@/lib/utils'
 
1
  import Button from '@/components/ui/Button'
2
  import { SiteInfo } from '@/lib/constants'
3
+ import ThemeToggle from '@/components/graph/ThemeToggle'
4
  import { TabsList, TabsTrigger } from '@/components/ui/Tabs'
5
  import { useSettingsStore } from '@/stores/settings'
6
  import { cn } from '@/lib/utils'
lightrag_webui/src/hooks/useTheme.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { useContext } from 'react'
2
- import { ThemeProviderContext } from '@/components/ThemeProvider'
3
 
4
  const useTheme = () => {
5
  const context = useContext(ThemeProviderContext)
 
1
  import { useContext } from 'react'
2
+ import { ThemeProviderContext } from '@/components/graph/ThemeProvider'
3
 
4
  const useTheme = () => {
5
  const context = useContext(ThemeProviderContext)