diff --git a/lightrag_webui/package.json b/lightrag_webui/package.json index 6016174c..7ed9ffcb 100644 --- a/lightrag_webui/package.json +++ b/lightrag_webui/package.json @@ -8,105 +8,98 @@ "build": "bunx --bun vite build", "lint": "eslint .", "preview": "bunx --bun vite preview", - "test": "bun test", - "test:watch": "bun test --watch", - "test:coverage": "bun test --coverage", "dev-no-bun": "vite", "build-no-bun": "vite build --emptyOutDir", "preview-no-bun": "vite preview" }, "dependencies": { - "@faker-js/faker": "^9.9.0", - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-checkbox": "^1.3.3", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-progress": "^1.1.8", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", - "@radix-ui/react-use-controllable-state": "^1.2.2", - "@react-sigma/core": "^5.0.4", - "@react-sigma/graph-search": "^5.0.4", - "@react-sigma/layout-circlepack": "^5.0.4", - "@react-sigma/layout-circular": "^5.0.4", - "@react-sigma/layout-force": "^5.0.4", - "@react-sigma/layout-forceatlas2": "^5.0.4", - "@react-sigma/layout-noverlap": "^5.0.4", - "@react-sigma/layout-random": "^5.0.4", - "@react-sigma/minimap": "^5.0.5", + "@faker-js/faker": "^9.5.0", + "@radix-ui/react-alert-dialog": "^1.1.6", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.2", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-tooltip": "^1.1.8", + "@radix-ui/react-use-controllable-state": "^1.1.0", + "@react-sigma/core": "^5.0.2", + "@react-sigma/graph-search": "^5.0.3", + "@react-sigma/layout-circlepack": "^5.0.2", + "@react-sigma/layout-circular": "^5.0.2", + "@react-sigma/layout-force": "^5.0.2", + "@react-sigma/layout-forceatlas2": "^5.0.2", + "@react-sigma/layout-noverlap": "^5.0.2", + "@react-sigma/layout-random": "^5.0.2", + "@react-sigma/minimap": "^5.0.2", "@sigma/edge-curve": "^3.1.0", "@sigma/node-border": "^3.0.0", - "@tanstack/react-table": "^8.21.3", - "axios": "^1.13.2", + "axios": "^1.7.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "^1.1.1", + "cmdk": "^1.0.4", "graphology": "^0.26.0", "graphology-generators": "^0.11.2", "graphology-layout": "^0.6.1", "graphology-layout-force": "^0.2.4", "graphology-layout-forceatlas2": "^0.10.1", "graphology-layout-noverlap": "^0.4.2", - "i18next": "^25.6.3", - "katex": "^0.16.25", - "mermaid": "^11.12.1", - "lucide-react": "^0.554.0", - "minisearch": "^7.2.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-dropzone": "^14.3.8", - "react-error-boundary": "^6.0.0", - "react-i18next": "^16.2.3", + "i18next": "^24.2.2", + "katex": "^0.16.22", + "lucide-react": "^0.475.0", + "mermaid": "^11.9.0", + "minisearch": "^7.1.2", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-dropzone": "^14.3.6", + "react-error-boundary": "^5.0.0", + "react-i18next": "^15.4.1", "react-markdown": "^9.1.0", - "react-number-format": "^5.4.4", - "react-router-dom": "^7.9.6", + "react-number-format": "^5.4.3", + "react-router-dom": "^7.3.0", "react-select": "^5.10.2", - "react-syntax-highlighter": "^15.6.6", + "react-syntax-highlighter": "^15.6.1", "rehype-katex": "^7.0.1", - "rehype-raw": "^7.0.0", "rehype-react": "^8.0.0", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "seedrandom": "^3.0.5", - "sigma": "^3.0.2", + "sigma": "^3.0.1", "sonner": "^1.7.4", - "tailwind-merge": "^3.4.0", - "tailwind-scrollbar": "^4.0.2", + "tailwind-merge": "^3.0.2", + "tailwind-scrollbar": "^4.0.1", "typography": "^0.16.24", - "unist-util-visit": "^5.0.0", - "zustand": "^5.0.8" + "zustand": "^5.0.3" }, "devDependencies": { - "@eslint/js": "^9.39.1", - "@stylistic/eslint-plugin-js": "^4.4.1", - "@types/bun": "^1.3.3", - "@tailwindcss/vite": "^4.1.17", + "@eslint/js": "^9.21.0", + "@stylistic/eslint-plugin-js": "^3.1.0", + "@tailwindcss/vite": "^4.0.8", + "@types/bun": "^1.2.3", "@types/katex": "^0.16.7", - "@types/node": "^22.18.9", - "@tailwindcss/typography": "^0.5.15", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", + "@types/node": "^22.13.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/react-i18next": "^8.1.0", "@types/react-syntax-highlighter": "^15.5.13", "@types/seedrandom": "^3.0.8", - "@vitejs/plugin-react-swc": "^4.2.2", - "eslint": "^9.39.1", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", - "globals": "^16.5.0", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.21.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", "graphology-types": "^0.24.8", - "prettier": "^3.6.2", - "prettier-plugin-tailwindcss": "^0.7.1", - "typescript-eslint": "^8.48.0", - "tailwindcss": "^4.1.17", + "prettier": "^3.5.2", + "prettier-plugin-tailwindcss": "^0.6.11", + "tailwindcss": "^4.0.8", "tailwindcss-animate": "^1.0.7", - "typescript": "~5.9.3", - "vite": "^7.1.12" + "typescript": "~5.7.3", + "typescript-eslint": "^8.24.1", + "vite": "^6.1.1" } } diff --git a/lightrag_webui/src/components/graph/GraphControl.tsx b/lightrag_webui/src/components/graph/GraphControl.tsx index 20d6d603..6d040c47 100644 --- a/lightrag_webui/src/components/graph/GraphControl.tsx +++ b/lightrag_webui/src/components/graph/GraphControl.tsx @@ -2,7 +2,7 @@ import { useRegisterEvents, useSetSettings, useSigma } from '@react-sigma/core' import { AbstractGraph } from 'graphology-types' // import { useLayoutCircular } from '@react-sigma/layout-circular' import { useLayoutForceAtlas2 } from '@react-sigma/layout-forceatlas2' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' // import useRandomGraph, { EdgeType, NodeType } from '@/hooks/useRandomGraph' import { EdgeType, NodeType } from '@/hooks/useLightragGraph' @@ -43,6 +43,20 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) const selectedEdge = useGraphStore.use.selectedEdge() const focusedEdge = useGraphStore.use.focusedEdge() const sigmaGraph = useGraphStore.use.sigmaGraph() + + // Track system theme changes when theme is set to 'system' + const [systemThemeIsDark, setSystemThemeIsDark] = useState(() => + window.matchMedia('(prefers-color-scheme: dark)').matches + ) + + useEffect(() => { + if (theme === 'system') { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + const handler = (e: MediaQueryListEvent) => setSystemThemeIsDark(e.matches) + mediaQuery.addEventListener('change', handler) + return () => mediaQuery.removeEventListener('change', handler) + } + }, [theme]) /** * When component mount or maxIterations changes @@ -204,7 +218,9 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) * => Setting the sigma reducers */ useEffect(() => { - const isDarkTheme = theme === 'dark' + // Check if dark mode is actually applied (handles both 'dark' theme and 'system' theme when OS is dark) + const isDarkTheme = theme === 'dark' || + (theme === 'system' && window.document.documentElement.classList.contains('dark')) const labelColor = isDarkTheme ? Constants.labelColorDarkTheme : undefined const edgeColor = isDarkTheme ? Constants.edgeColorDarkTheme : undefined @@ -329,6 +345,7 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) sigma, disableHoverEffect, theme, + systemThemeIsDark, hideUnselectedEdges, enableEdgeEvents, renderEdgeLabels, diff --git a/lightrag_webui/src/components/graph/Settings.tsx b/lightrag_webui/src/components/graph/Settings.tsx index 37a36305..41da2f22 100644 --- a/lightrag_webui/src/components/graph/Settings.tsx +++ b/lightrag_webui/src/components/graph/Settings.tsx @@ -7,8 +7,10 @@ import Input from '@/components/ui/Input' import { controlButtonVariant } from '@/lib/constants' import { useSettingsStore } from '@/stores/settings' +import { useGraphStore } from '@/stores/graph' +import useRandomGraph from '@/hooks/useRandomGraph' -import { SettingsIcon, Undo2 } from 'lucide-react' +import { SettingsIcon, Undo2, Shuffle } from 'lucide-react' import { useTranslation } from 'react-i18next'; /** @@ -163,6 +165,9 @@ export default function Settings() { const enableHealthCheck = useSettingsStore.use.enableHealthCheck() + // Random graph functionality for development/testing + const { randomGraph } = useRandomGraph() + const setEnableNodeDrag = useCallback( () => useSettingsStore.setState((pre) => ({ enableNodeDrag: !pre.enableNodeDrag })), [] @@ -228,6 +233,11 @@ export default function Settings() { useSettingsStore.setState({ graphLayoutMaxIterations: iterations }) }, []) + const handleGenerateRandomGraph = useCallback(() => { + const graph = randomGraph() + useGraphStore.getState().setSigmaGraph(graph) + }, [randomGraph]) + const { t } = useTranslation(); const saveSettings = () => setOpened(false); @@ -376,6 +386,24 @@ export default function Settings() { defaultValue={15} onEditFinished={setGraphLayoutMaxIterations} /> + + + {/* Development/Testing Section */} +
+ + +
+