diff --git a/.dockerignore b/.dockerignore index e15009e..07a1ae9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,8 +14,10 @@ *~ # 构建产物 -# bin/agents/ 和 web/dist/ 不能忽略,运行时从本地文件加载 +# bin/agents/、web/dist/ 和 themes/ 不能忽略,运行时从本地文件加载 web/node_modules/ +# CI 仅使用该 checkout 构建 themes/default,不应进入镜像上下文 +pika-default-theme # 数据目录 data/ diff --git a/.github/default-theme.ref b/.github/default-theme.ref new file mode 100644 index 0000000..689f51d --- /dev/null +++ b/.github/default-theme.ref @@ -0,0 +1 @@ +04ff9915a411debd851bfadba9e28f7294ec910c diff --git a/.github/workflows/build-test-ghcr.yml b/.github/workflows/build-test-ghcr.yml index b22b14f..910281c 100644 --- a/.github/workflows/build-test-ghcr.yml +++ b/.github/workflows/build-test-ghcr.yml @@ -23,6 +23,17 @@ jobs: - name: 检出代码 uses: actions/checkout@v4 + - name: 读取默认主题锁定版本 + run: echo "DEFAULT_THEME_REF=$(tr -d '\r\n' < .github/default-theme.ref)" >> "$GITHUB_ENV" + + # 默认主题作为独立源码仓库参与 Pika 发布构建 + - name: 检出官方默认主题 + uses: actions/checkout@v4 + with: + repository: pika-monitor/pika-default-theme + ref: ${{ env.DEFAULT_THEME_REF }} + path: pika-default-theme + # 安装 make 和 upx - name: 安装 make 和 upx run: | @@ -44,7 +55,7 @@ jobs: # 构建所有组件(前端、agents、服务端) - name: 构建所有组件 run: | - make build-web + make DEFAULT_THEME_DIR=pika-default-theme build-web make build-agents make build-servers diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index fe87d4a..829a527 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -31,6 +31,17 @@ jobs: - name: 检出代码 uses: actions/checkout@v4 + - name: 读取默认主题锁定版本 + run: echo "DEFAULT_THEME_REF=$(tr -d '\r\n' < .github/default-theme.ref)" >> "$GITHUB_ENV" + + # 默认主题作为独立源码仓库参与 Pika 发布构建 + - name: 检出官方默认主题 + uses: actions/checkout@v4 + with: + repository: pika-monitor/pika-default-theme + ref: ${{ env.DEFAULT_THEME_REF }} + path: pika-default-theme + # 安装 make 和 upx - name: 安装 make 和 upx run: | @@ -52,7 +63,7 @@ jobs: # 构建所有组件(前端、agents、服务端) - name: 构建所有组件 run: | - make build-release + make DEFAULT_THEME_DIR=pika-default-theme build-release # 列出编译产物 - name: 列出编译产物 diff --git a/.gitignore b/.gitignore index caedb3f..c97aa99 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ agent.yaml playground web/node_modules web/dist -deploy.sh \ No newline at end of file +themes/default +deploy.sh diff --git a/Dockerfile b/Dockerfile index c343803..e586104 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ ARG TARGETARCH COPY ./bin/pika-linux-${TARGETARCH} ./pika COPY ./bin/agents ./bin/agents COPY ./web/dist ./web/dist +COPY ./themes ./themes # 暴露端口 EXPOSE 8080 diff --git a/Makefile b/Makefile index 993263c..71013ae 100644 --- a/Makefile +++ b/Makefile @@ -17,12 +17,27 @@ LDFLAGS=-s -w -X 'github.com/pika-monitor/pika/pkg/version.Version=$(VERSION)' - AGENT_LDFLAGS=-s -w -X 'github.com/pika-monitor/pika/pkg/version.Version=$(VERSION)' -X 'github.com/pika-monitor/pika/pkg/version.AgentVersion=$(AGENT_VERSION)' GOFLAGS=CGO_ENABLED=0 -# 构建前端 +# 默认主题已拆分到独立仓库。本地默认使用同级目录,CI 会显式传入 checkout 目录。 +DEFAULT_THEME_DIR ?= ../pika-default-theme +DEFAULT_THEME_OUTPUT_DIR ?= themes/default + +.PHONY: build-web build-default-theme + +# 构建官方管理前端和独立的默认主题,并组装到统一发布目录。 build-web: npm ci --prefix web npm run build --prefix web - npm ci --prefix portal - npm run build --prefix portal + $(MAKE) build-default-theme + +build-default-theme: + test -f "$(DEFAULT_THEME_DIR)/package-lock.json" + test -f "$(DEFAULT_THEME_DIR)/pika-theme.json" + npm ci --prefix "$(DEFAULT_THEME_DIR)" + npm run build --prefix "$(DEFAULT_THEME_DIR)" + rm -rf "$(DEFAULT_THEME_OUTPUT_DIR)" + mkdir -p "$(DEFAULT_THEME_OUTPUT_DIR)" + cp "$(DEFAULT_THEME_DIR)/pika-theme.json" "$(DEFAULT_THEME_OUTPUT_DIR)/pika-theme.json" + cp -R "$(DEFAULT_THEME_DIR)/dist" "$(DEFAULT_THEME_OUTPUT_DIR)/dist" # 构建服务端(开发) build-server: @@ -67,14 +82,15 @@ build-agents: # 构建所有(release版本) build-release: - make build-web - make build-agents - make build-servers + $(MAKE) build-web + $(MAKE) build-agents + $(MAKE) build-servers # 清理编译产物 clean: rm -rf bin/* - rm -rf web/dist portal/dist + rm -rf web/dist + rm -rf themes/default # 运行测试 test: diff --git a/internal/app.go b/internal/app.go index 310bf87..d94f61b 100644 --- a/internal/app.go +++ b/internal/app.go @@ -4,9 +4,11 @@ import ( "context" "log" "net/http" + "path/filepath" "time" "github.com/google/uuid" + "github.com/pika-monitor/pika/internal/assets" "github.com/pika-monitor/pika/internal/config" "github.com/pika-monitor/pika/internal/handler" "github.com/pika-monitor/pika/internal/migrate" @@ -275,9 +277,9 @@ func setupApi(app *orz.App, components *AppComponents) error { // GitHub 认证路由(如果启用) publicApi.POST("/auth/github/callback", components.AccountHandler.GitHubLogin) - // 官方管理 SPA 与活动公开主题使用互不重叠的资源前缀。 - e.GET("/admin/assets/*", components.WebHandler.ServeAdminAsset) - e.GET("/theme-assets/*", components.WebHandler.ServeThemeAsset) + // 管理前端从 web/dist 由 Echo 直接加载;活动公开主题由主题服务选择独立目录。 + e.Static("/admin/assets/", filepath.Join(assets.WebDir(), "assets"), handler.ImmutableStaticHeaders) + e.GET("/t/*", components.WebHandler.ServeThemeAsset) e.GET("/*", components.WebHandler.ServeSPA) return nil diff --git a/internal/assets/paths.go b/internal/assets/paths.go index 130aad4..ebed8cb 100644 --- a/internal/assets/paths.go +++ b/internal/assets/paths.go @@ -8,8 +8,8 @@ import ( const ( defaultWebDir = "web/dist" + defaultThemeDir = "themes/default" defaultAgentDir = "bin/agents" - defaultLogoPath = "portal/public/logo.png" ) func WebDir() string { @@ -19,6 +19,13 @@ func WebDir() string { return defaultWebDir } +func DefaultThemeDir() string { + if dir := os.Getenv("PIKA_DEFAULT_THEME_DIR"); dir != "" { + return dir + } + return defaultThemeDir +} + func AgentDir() string { if dir := os.Getenv("PIKA_AGENT_DIR"); dir != "" { return dir @@ -33,7 +40,7 @@ func AgentPath(filename string) string { func DefaultLogoBase64() string { for _, path := range []string{ os.Getenv("PIKA_DEFAULT_LOGO_PATH"), - defaultLogoPath, + filepath.Join(DefaultThemeDir(), "dist", "logo.png"), filepath.Join(WebDir(), "logo.png"), } { if path == "" { diff --git a/internal/handler/web_handler.go b/internal/handler/web_handler.go index 7c20f38..40ce29d 100644 --- a/internal/handler/web_handler.go +++ b/internal/handler/web_handler.go @@ -31,16 +31,12 @@ func (h *WebHandler) PublicConfig(c *echo.Context) error { return orz.Ok(c, runtime) } -func (h *WebHandler) ServeAdminAsset(c *echo.Context) error { - return serveStaticFile(c, filepath.Join(assets.WebDir(), "admin", "assets"), c.Param("*"), true) -} - func (h *WebHandler) ServeThemeAsset(c *echo.Context) error { _, dist, err := h.themeService.ActiveDistDir(c.Request().Context()) if err != nil { return err } - return serveStaticFile(c, dist, c.Param("*"), true) + return serveStaticFile(c, dist, c.Param("*"), false) } func (h *WebHandler) ServeSPA(c *echo.Context) error { @@ -74,13 +70,23 @@ func isAdminPage(path string) bool { } func (h *WebHandler) serveAdminIndex(c *echo.Context) error { - path := filepath.Join(assets.WebDir(), "admin", "index.html") - data, err := os.ReadFile(path) - if err != nil { + root := assets.WebDir() + path := filepath.Join(root, "index.html") + if stat, err := os.Stat(path); err != nil || !stat.Mode().IsRegular() { return echo.NewHTTPError(http.StatusInternalServerError, "官方管理前端缺失") } setHTMLHeaders(c) - return c.Blob(http.StatusOK, "text/html; charset=utf-8", data) + return c.FileFS("index.html", os.DirFS(root)) +} + +// ImmutableStaticHeaders 为带内容摘要文件名的前端构建产物设置长期缓存和安全响应头。 +func ImmutableStaticHeaders(next echo.HandlerFunc) echo.HandlerFunc { + return func(c *echo.Context) error { + c.Response().Header().Set("Cache-Control", "public, max-age=31536000, immutable") + c.Response().Header().Set("X-Content-Type-Options", "nosniff") + c.Response().Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") + return next(c) + } } func (h *WebHandler) serveThemeIndex(c *echo.Context) error { diff --git a/internal/handler/web_handler_test.go b/internal/handler/web_handler_test.go index 48fafbd..82a3866 100644 --- a/internal/handler/web_handler_test.go +++ b/internal/handler/web_handler_test.go @@ -91,20 +91,20 @@ func TestResolveStaticPathAndHeaders(t *testing.T) { func TestEchoWildcardRoutesKeepAdminAndAPISeparated(t *testing.T) { webRoot := t.TempDir() - if err := os.MkdirAll(filepath.Join(webRoot, "admin", "assets"), 0o755); err != nil { + if err := os.MkdirAll(filepath.Join(webRoot, "assets"), 0o755); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(webRoot, "admin", "assets", "app.js"), []byte("admin"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(webRoot, "assets", "app.js"), []byte("admin"), 0o644); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(webRoot, "admin", "index.html"), []byte("admin"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(webRoot, "index.html"), []byte("admin"), 0o644); err != nil { t.Fatal(err) } t.Setenv("PIKA_WEB_DIR", webRoot) e := echo.New() handler := &WebHandler{} - e.GET("/admin/assets/*", handler.ServeAdminAsset) + e.Static("/admin/assets/", filepath.Join(webRoot, "assets"), ImmutableStaticHeaders) e.GET("/*", handler.ServeSPA) assetRecorder := httptest.NewRecorder() @@ -112,6 +112,9 @@ func TestEchoWildcardRoutesKeepAdminAndAPISeparated(t *testing.T) { if assetRecorder.Code != http.StatusOK || assetRecorder.Body.String() != "admin" { t.Fatalf("wildcard asset route failed: status=%d body=%q", assetRecorder.Code, assetRecorder.Body.String()) } + if assetRecorder.Header().Get("Cache-Control") != "public, max-age=31536000, immutable" { + t.Fatalf("admin asset missing immutable cache header: %#v", assetRecorder.Header()) + } adminRecorder := httptest.NewRecorder() e.ServeHTTP(adminRecorder, httptest.NewRequest(http.MethodGet, "/admin/agents", nil)) diff --git a/internal/service/theme.go b/internal/service/theme.go index 72e95c4..69d98c0 100644 --- a/internal/service/theme.go +++ b/internal/service/theme.go @@ -83,7 +83,7 @@ var ( themeIDPattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$`) reservedThemeIDs = map[string]struct{}{ "default": {}, "admin": {}, "official": {}, "system": {}, - "api": {}, "assets": {}, "theme-assets": {}, + "api": {}, "assets": {}, "t": {}, "theme-assets": {}, } requiredThemeCaps = []string{"server-list", "server-detail", "monitor-list", "monitor-detail"} titleElement = regexp.MustCompile(`(?is)]*)?>.*?`) @@ -123,7 +123,7 @@ func NewThemeService(logger *zap.Logger, propertyService *PropertyService, cfg * logger: logger, propertyService: propertyService, themeDir: themeDir, - defaultThemeDir: "portal", + defaultThemeDir: assets.DefaultThemeDir(), } if err := os.MkdirAll(filepath.Join(themeDir, ".staging"), 0755); err != nil { return nil, fmt.Errorf("创建主题目录失败: %w", err) diff --git a/internal/service/theme_service_test.go b/internal/service/theme_service_test.go index 21e20ef..bf8c674 100644 --- a/internal/service/theme_service_test.go +++ b/internal/service/theme_service_test.go @@ -168,6 +168,7 @@ func TestThemeInstallActivateDeleteLifecycle(t *testing.T) { webRoot := t.TempDir() writeOfficialWebBuild(t, webRoot) t.Setenv("PIKA_WEB_DIR", webRoot) + t.Setenv("PIKA_DEFAULT_THEME_DIR", filepath.Join(webRoot, "default-theme")) themeDir := filepath.Join(t.TempDir(), "themes") database, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) @@ -267,13 +268,10 @@ func makeInstallableThemeZIP(t *testing.T, manifest ThemeManifest) []byte { func writeOfficialWebBuild(t *testing.T, webRoot string) { t.Helper() - if err := os.MkdirAll(filepath.Join(webRoot, "admin", "assets"), 0o755); err != nil { + if err := os.MkdirAll(filepath.Join(webRoot, "assets"), 0o755); err != nil { t.Fatal(err) } - if err := os.MkdirAll(filepath.Join(webRoot, "admin"), 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(webRoot, "admin", "index.html"), []byte("admin"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(webRoot, "index.html"), []byte("admin"), 0o644); err != nil { t.Fatal(err) } writeThemeRoot(t, filepath.Join(webRoot, "default-theme"), validTestManifest(DefaultThemeID)) diff --git a/internal/service/theme_util.go b/internal/service/theme_util.go index 10ce42d..eb83790 100644 --- a/internal/service/theme_util.go +++ b/internal/service/theme_util.go @@ -18,13 +18,13 @@ import ( "golang.org/x/text/unicode/norm" ) -// validateOfficialWebBuild 启动时验证官方 web/dist 完整性。 +// validateOfficialWebBuild 启动时验证官方管理前端和默认主题的发布产物完整性。 func validateOfficialWebBuild(webDir, defaultThemeDir string) error { if version.GetVersion() == "dev" && strings.TrimSpace(os.Getenv("PIKA_WEB_DIR")) == "" { return nil } requiredFiles := []string{ - filepath.Join(webDir, "admin", "index.html"), + filepath.Join(webDir, "index.html"), filepath.Join(defaultThemeDir, "pika-theme.json"), filepath.Join(defaultThemeDir, "dist", "index.html"), } @@ -34,9 +34,9 @@ func validateOfficialWebBuild(webDir, defaultThemeDir string) error { return fmt.Errorf("官方 Web 构建缺失: %s", path) } } - assetStat, err := os.Stat(filepath.Join(webDir, "admin", "assets")) + assetStat, err := os.Stat(filepath.Join(webDir, "assets")) if err != nil || !assetStat.IsDir() { - return fmt.Errorf("官方管理静态资源目录缺失: %s", filepath.Join(webDir, "admin", "assets")) + return fmt.Errorf("官方管理静态资源目录缺失: %s", filepath.Join(webDir, "assets")) } manifest, err := loadThemeManifest(filepath.Join(defaultThemeDir, "pika-theme.json")) if err != nil { diff --git a/portal/.gitignore b/portal/.gitignore deleted file mode 100644 index 0e8e171..0000000 --- a/portal/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -dist -dist-ssr -*.local -.DS_Store -yarn.lock diff --git a/portal/eslint.config.js b/portal/eslint.config.js deleted file mode 100644 index d9b4352..0000000 --- a/portal/eslint.config.js +++ /dev/null @@ -1,14 +0,0 @@ -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default [ - {ignores: ['dist', 'node_modules']}, - { - files: ['src/**/*.{ts,tsx}'], - languageOptions: { - parser: tseslint.parser, - parserOptions: {ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: {jsx: true}}, - globals: {...globals.browser, ...globals.es2022}, - }, - }, -]; diff --git a/portal/index.html b/portal/index.html deleted file mode 100644 index 02bcbc6..0000000 --- a/portal/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Pika Monitor - - - -
- - - - diff --git a/portal/package-lock.json b/portal/package-lock.json deleted file mode 100644 index 141c3e7..0000000 --- a/portal/package-lock.json +++ /dev/null @@ -1,3618 +0,0 @@ -{ - "name": "@pika/default-theme", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@pika/default-theme", - "version": "1.0.0", - "dependencies": { - "@radix-ui/react-tooltip": "^1.2.12", - "@tanstack/react-query": "^5.101.2", - "clsx": "^2.1.1", - "dayjs": "^1.11.21", - "lucide-react": "^1.24.0", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router-dom": "^7.18.1", - "recharts": "^3.9.2", - "tailwind-merge": "^3.6.0", - "tw-animate-css": "^1.4.0" - }, - "devDependencies": { - "@tailwindcss/vite": "^4.3.2", - "@types/node": "^26.1.1", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.3", - "eslint": "^10.7.0", - "globals": "^17.7.0", - "typescript": "~6.0.3", - "typescript-eslint": "^8.63.0", - "vite": "8.1.4" - } - }, - "../theme-sdk": { - "name": "@pika-monitor/theme-sdk", - "version": "1.0.0", - "extraneous": true, - "devDependencies": { - "typescript": "~6.0.3" - }, - "peerDependencies": { - "typescript": ">=5.0.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", - "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", - "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.12" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", - "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.8.0", - "@floating-ui/utils": "^0.2.12" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", - "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.8.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", - "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", - "license": "MIT" - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", - "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.139.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", - "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", - "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", - "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", - "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", - "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", - "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-effect-event": "0.0.5" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", - "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", - "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.15", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-layout-effect": "1.1.4", - "@radix-ui/react-use-rect": "1.1.4", - "@radix-ui/react-use-size": "1.1.4", - "@radix-ui/rect": "1.1.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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", - "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", - "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", - "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.3.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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", - "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz", - "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-popper": "1.3.7", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-layout-effect": "1.1.4", - "@radix-ui/react-visually-hidden": "1.2.11" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", - "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", - "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-use-effect-event": "0.0.5", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", - "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", - "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", - "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/rect": "1.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", - "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", - "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10" - }, - "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" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", - "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", - "license": "MIT" - }, - "node_modules/@reduxjs/toolkit": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", - "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@standard-schema/utils": "^0.3.0", - "immer": "^11.0.0", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", - "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", - "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", - "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", - "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", - "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", - "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", - "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", - "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", - "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", - "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", - "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", - "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", - "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", - "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", - "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", - "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.24.1", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", - "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-x64": "4.3.3", - "@tailwindcss/oxide-freebsd-x64": "4.3.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-x64-musl": "4.3.3", - "@tailwindcss/oxide-wasm32-wasi": "4.3.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", - "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", - "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", - "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", - "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", - "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", - "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", - "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", - "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", - "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", - "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", - "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", - "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", - "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.3.3", - "@tailwindcss/oxide": "4.3.3", - "tailwindcss": "4.3.3" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@tanstack/query-core": { - "version": "5.101.4", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", - "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.101.4", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", - "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.101.4" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~8.3.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", - "devOptional": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", - "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", - "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.65.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", - "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "^1.0.1" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/dayjs": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", - "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", - "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-toolkit": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", - "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==", - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks", - "tests/types" - ] - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", - "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", - "dev": true, - "license": "MIT", - "workspaces": [ - "packages/*" - ], - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.7.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.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", - "minimatch": "^10.2.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", - "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz", - "integrity": "sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "11.1.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz", - "integrity": "sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lucide-react": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.28.0.tgz", - "integrity": "sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", - "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.16", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.8" - } - }, - "node_modules/react-is": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", - "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", - "license": "MIT", - "peer": true - }, - "node_modules/react-redux": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", - "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", - "license": "MIT", - "dependencies": { - "@types/use-sync-external-store": "^0.0.6", - "use-sync-external-store": "^1.4.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25 || ^19", - "react": "^18.0 || ^19", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-router": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", - "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", - "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", - "license": "MIT", - "dependencies": { - "react-router": "7.18.2" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/recharts": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", - "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", - "license": "MIT", - "workspaces": [ - "www" - ], - "dependencies": { - "@reduxjs/toolkit": "^1.9.0 || 2.x.x", - "clsx": "^2.1.1", - "decimal.js-light": "^2.5.1", - "es-toolkit": "^1.39.3", - "eventemitter3": "^5.0.1", - "immer": "^11.1.8", - "react-redux": "8.x.x || 9.x.x", - "reselect": "5.2.0", - "tiny-invariant": "^1.3.3", - "use-sync-external-store": "^1.2.2", - "victory-vendor": "^37.0.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" - }, - "node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", - "license": "MIT", - "peerDependencies": { - "redux": "^5.0.0" - } - }, - "node_modules/reselect": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", - "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", - "license": "MIT" - }, - "node_modules/rolldown": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", - "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.139.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.5", - "@rolldown/binding-darwin-arm64": "1.1.5", - "@rolldown/binding-darwin-x64": "1.1.5", - "@rolldown/binding-freebsd-x64": "1.1.5", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", - "@rolldown/binding-linux-arm64-gnu": "1.1.5", - "@rolldown/binding-linux-arm64-musl": "1.1.5", - "@rolldown/binding-linux-ppc64-gnu": "1.1.5", - "@rolldown/binding-linux-s390x-gnu": "1.1.5", - "@rolldown/binding-linux-x64-gnu": "1.1.5", - "@rolldown/binding-linux-x64-musl": "1.1.5", - "@rolldown/binding-openharmony-arm64": "1.1.5", - "@rolldown/binding-wasm32-wasi": "1.1.5", - "@rolldown/binding-win32-arm64-msvc": "1.1.5", - "@rolldown/binding-win32-x64-msvc": "1.1.5" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tailwind-merge": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", - "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", - "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/tw-animate-css": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", - "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Wombosvideo" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", - "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/victory-vendor": { - "version": "37.3.6", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", - "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/vite": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", - "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.16", - "rolldown": "~1.1.4", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/portal/package.json b/portal/package.json deleted file mode 100644 index 7375a3f..0000000 --- a/portal/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pika/default-theme", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint src" - }, - "dependencies": { - "@radix-ui/react-tooltip": "^1.2.12", - "@tanstack/react-query": "^5.101.2", - "clsx": "^2.1.1", - "dayjs": "^1.11.21", - "lucide-react": "^1.24.0", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router-dom": "^7.18.1", - "recharts": "^3.9.2", - "tailwind-merge": "^3.6.0", - "tw-animate-css": "^1.4.0" - }, - "devDependencies": { - "@tailwindcss/vite": "^4.3.2", - "@types/node": "^26.1.1", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.3", - "eslint": "^10.7.0", - "globals": "^17.7.0", - "typescript": "~6.0.3", - "typescript-eslint": "^8.63.0", - "vite": "8.1.4" - } -} diff --git a/portal/pika-theme.json b/portal/pika-theme.json deleted file mode 100644 index 8fcf07a..0000000 --- a/portal/pika-theme.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "schemaVersion": 1, - "id": "default", - "name": "Pika Official", - "description": "Pika 官方默认公开主题", - "version": "1.0.0", - "author": "dushixiang", - "homepage": "https://github.com/dushixiang/pika", - "license": "MIT", - "preview": "dist/logo.png", - "entry": "dist/index.html", - "apiVersion": "v1", - "capabilities": ["server-list", "server-detail", "monitor-list", "monitor-detail"] -} diff --git a/portal/public/logo.png b/portal/public/logo.png deleted file mode 100644 index 0e3c2fd..0000000 Binary files a/portal/public/logo.png and /dev/null differ diff --git a/portal/src/App.tsx b/portal/src/App.tsx deleted file mode 100644 index 5ee5a49..0000000 --- a/portal/src/App.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import {lazy, Suspense} from 'react'; -import {BrowserRouter, Route, Routes} from 'react-router-dom'; -import {ColorModeProvider} from './contexts/ColorMode'; - -const PublicLayout = lazy(() => import('./pages/PublicLayout')); -const ServerList = lazy(() => import('./pages/ServerList')); -const ServerDetail = lazy(() => import('./pages/ServerDetail')); -const MonitorList = lazy(() => import('./pages/MonitorList')); -const MonitorDetail = lazy(() => import('./pages/MonitorDetail')); - -export default function PortalApp() { - return ( - - - 页面加载中...}> - - }> - }/> - }/> - }/> - }/> - - - - - - ); -} diff --git a/portal/src/api.ts b/portal/src/api.ts deleted file mode 100644 index 4cef8b9..0000000 --- a/portal/src/api.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* Pika 公开 API 客户端。自动拼接 /api 前缀并从 localStorage 读取登录 token。 */ - -import type { - PikaRuntimeConfig, ColorMode, - Agent, LatestMetrics, PublicMonitor, AgentMonitorStat, - MetricsResponse, MetricsParams, HistoryParams, - TagsResponse, NetworkInterfacesResponse, ListAgentParams, CurrentUser, -} from './types'; - -export class PikaAPIError extends Error { - constructor(public status: number, message: string, public data?: unknown) { - super(message); - this.name = 'PikaAPIError'; - } -} - -type Query = Record; - -const request = async (path: string, query?: Query): Promise => { - const search = new URLSearchParams(); - for (const [key, value] of Object.entries(query || {})) { - if (value !== undefined) search.set(key, String(value)); - } - const token = typeof localStorage === 'undefined' ? null : localStorage.getItem('token'); - const response = await fetch('/api' + path + (search.size ? '?' + search : ''), { - headers: token ? {Authorization: 'Bearer ' + token} : {}, - }); - const contentType = response.headers.get('content-type') || ''; - const data: unknown = contentType.includes('application/json') ? await response.json() : await response.text(); - if (!response.ok) { - const message = typeof data === 'object' && data !== null && 'message' in data && typeof data.message === 'string' - ? data.message - : 'Pika API 请求失败'; - throw new PikaAPIError(response.status, message, data); - } - return data as T; -}; - -export const pika = { - getConfig: () => request('/config'), - listAgents: (params: ListAgentParams = {}) => request('/agents', params), - getAgent: (id: string) => request('/agents/' + encodeURIComponent(id)), - getLatestMetrics: (id: string) => - request('/agents/' + encodeURIComponent(id) + '/metrics/latest'), - getMetrics: (id: string, params: MetricsParams = {}) => - request('/agents/' + encodeURIComponent(id) + '/metrics', params), - getTags: () => request('/agents/tags'), - getNetworkInterfaces: (id: string) => - request('/agents/' + encodeURIComponent(id) + '/network-interfaces'), - listMonitors: () => request('/monitors'), - getMonitorStats: >(id: string) => - request('/monitors/' + encodeURIComponent(id) + '/stats'), - getMonitorAgents: (id: string) => - request('/monitors/' + encodeURIComponent(id) + '/agents'), - getMonitorHistory: (id: string, params: HistoryParams = {}) => - request('/monitors/' + encodeURIComponent(id) + '/history', params), - getCurrentUser: () => request('/admin/account/info'), -}; - -/* 运行时配置(后端在入口 HTML 注入 window.PikaRuntime) */ - -declare global { - interface Window {PikaRuntime?: PikaRuntimeConfig} -} - -export const getRuntimeConfig = (): PikaRuntimeConfig => { - if (typeof window === 'undefined' || !window.PikaRuntime) { - throw new Error('PikaRuntime 尚未注入'); - } - return window.PikaRuntime; -}; - -export const resolveColorMode = (mode: ColorMode): Exclude => { - if (mode !== 'system') return mode; - return typeof matchMedia !== 'undefined' && matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; -}; diff --git a/portal/src/components.tsx b/portal/src/components.tsx deleted file mode 100644 index 557eda4..0000000 --- a/portal/src/components.tsx +++ /dev/null @@ -1,768 +0,0 @@ -import {type ReactNode, useEffect, useState} from 'react'; -import {Activity, AlertCircle, Heart, Loader2, LogIn, Menu, Moon, ServerIcon, Settings, Sun, TrendingUp, X} from 'lucide-react'; -import {Link, useLocation} from 'react-router-dom'; -import dayjs from 'dayjs'; -import type {TimeRangeOption} from './types'; -import {getRuntimeConfig, pika} from './api'; -import {cn} from './lib/utils'; -import {useColorMode} from './contexts/ColorMode'; - -/* ========================================== Card ========================================== */ - -interface CardProps { - title?: ReactNode; - description?: ReactNode; - action?: ReactNode; - children: ReactNode; - className?: string; - interactive?: boolean; -} - -export const Card = ({title, description, action, children, className, interactive}: CardProps) => { - const hasHeader = title || description || action; - - return ( -
- - - - - {interactive && ( - - )} - -
- {hasHeader && ( -
-
- {title &&

{title}

} - {description &&

{description}

} -
- {action &&
{action}
} -
- )} -
- {children} -
-
-
- ); -}; - -/* ========================================== ChartPlaceholder ========================================== */ - -interface ChartPlaceholderProps { - icon?: typeof TrendingUp; - title?: string; - subtitle?: string; - heightClass?: string; - className?: string; -} - -export const ChartPlaceholder = ({ - icon: Icon = TrendingUp, - title = '暂无数据', - subtitle = '等待采集新数据后展示图表', - heightClass = 'h-52', - className, - }: ChartPlaceholderProps) => { - return ( -
-
- -

{title}

- {subtitle && ( -

- {subtitle} -

- )} -
-
- ); -}; - -/* ========================================== CustomTooltip ========================================== */ - -type CustomTooltipProps = { - active?: boolean; - payload?: Array<{ - name?: string; - value?: number; - color?: string; - dataKey?: string; - payload?: { - timestamp?: number | string; - [key: string]: unknown; - }; - }>; - label?: string | number; - unit?: string; - className?: string; - timeFormat?: string; -}; - -export const CustomTooltip = ({active, payload, label, unit = '%', className, timeFormat = 'MM-DD HH:mm'}: CustomTooltipProps) => { - if (!active || !payload || payload.length === 0) { - return null; - } - - // 从 payload 中获取完整的时间戳信息(如果有的话) - const fullTimestamp = payload[0]?.payload?.timestamp; - const displayLabel = fullTimestamp - ? dayjs(fullTimestamp).format(timeFormat) - : label; - - return ( -
-

- {displayLabel} -

-
- {payload.map((entry, index) => { - if (!entry) { - return null; - } - - const dotColor = entry.color ?? '#6366f1'; - const title = entry.name ?? entry.dataKey ?? `系列 ${index + 1}`; - const value = typeof entry.value === 'number' - ? Number.isFinite(entry.value) - ? entry.value.toFixed(2) - : '-' - : entry.value; - - return ( -

- - - {title}: {value}{unit} - -

- ); - })} -
-
- ); -}; - -/* ========================================== EmptyState ========================================== */ - -interface EmptyStateProps { - message?: string; - showBackButton?: boolean; - className?: string; -} - -export const EmptyState = ({message = '监控数据不存在', showBackButton = false, className}: EmptyStateProps) => { - return ( -
-
-
- -
-

- {message} -

- {showBackButton && ( - - )} -
-
- ); -}; - -/* ========================================== LoadingSpinner ========================================== */ - -interface LoadingSpinnerProps { - message?: string; -} - -export const LoadingSpinner = ({message}: LoadingSpinnerProps) => { - - return ( -
-
- -

- {message || '数据加载中,请稍候...'} -

-
-
- ); -}; - -/* ========================================== MetricItem ========================================== */ - -// 统计卡片组件 -export const MetricItem = ({ - label, - value, - }: { - label: string; - value: string | number; -}) => ( -
-

{label}

-

{value}

-
-); - -/* ========================================== StatBlock ========================================== */ - -// 统计卡片组件 -interface StatBlockProps { - title: string; - value: any; - unit?: string - icon: any; - color: string; - alert?: boolean, - glow?: boolean, -} - -export const StatBlock = ({title, value, unit, icon: Icon, color, alert, glow}: StatBlockProps) => { - - const colorMap = { - cyan: 'dark:text-cyan-400 dark:border-cyan-500/30 dark:bg-cyan-500/5', - emerald: 'dark:text-emerald-400 dark:border-emerald-500/30 dark:bg-emerald-500/5', - rose: 'dark:text-rose-400 dark:border-rose-500/30 dark:bg-rose-500/5', - purple: 'dark:text-purple-400 dark:border-purple-500/30 dark:bg-purple-500/5' - }; - const style = colorMap[color] || colorMap.cyan; - - const iconColor = { - cyan: 'text-cyan-400', - emerald: 'text-emerald-400', - rose: 'text-rose-400', - purple: 'text-purple-400' - } - let iconStyle = iconColor[color] || colorMap.cyan; - - return ( -
-
-
-
-
{title}
-
{value}{unit && - {unit}}
-
-
- -
-
-
- ); -}; - -/* ========================================== StatusBadge ========================================== */ - -interface StatusBadgeProps { - status: string; -} - -export const StatusBadge = ({status}: StatusBadgeProps) => { - const styles = { - up: "bg-emerald-500/10 dark:bg-emerald-500/10 text-emerald-400 dark:text-emerald-400 border-emerald-500/80", - down: "bg-rose-500/10 dark:bg-rose-500/10 text-rose-400 dark:text-rose-400 border-rose-500/80", - unknown: "bg-slate-100 dark:bg-slate-800 text-slate-400 dark:text-slate-300 border-slate-200 dark:border-slate-700", - }; - - const labels = { - up: "正常", - down: "异常", - unknown: "未知", - }; - - const style = styles[status as keyof typeof styles] || styles.unknown; - const label = labels[status as keyof typeof labels] || labels.unknown; - - - return ( - - - {label} - - - - ); -}; - -/* ========================================== PublicHeader ========================================== */ - -export const PublicHeader = () => { - const [isLoggedIn, setIsLoggedIn] = useState(false); - const [currentTime, setCurrentTime] = useState(new Date()); - const [mobileMenuOpen, setMobileMenuOpen] = useState(false); - const {resolvedColorMode: appliedTheme, setColorMode: setTheme} = useColorMode(); - let location = useLocation(); - - - useEffect(() => { - // 检查本地是否有 token - const token = localStorage.getItem('token'); - const userInfo = localStorage.getItem('userInfo'); - - if (!token || !userInfo) { - setIsLoggedIn(false); - return; - } - - // 调用后端接口验证 token 是否有效 - pika.getCurrentUser() - .then(() => { - setIsLoggedIn(true); - }) - .catch(() => { - // token 无效,清除本地存储 - localStorage.removeItem('token'); - localStorage.removeItem('userInfo'); - setIsLoggedIn(false); - }); - }, []); - - // 时钟特效 - useEffect(() => { - const t = setInterval(() => setCurrentTime(new Date()), 1000); - return () => clearInterval(t); - }, []); - - // 判断导航是否激活 - const currentPath = location.pathname; - - let activeTab = 'servers'; - - if (currentPath.startsWith('/monitors')) { - activeTab = 'monitors'; - } - - const runtime = getRuntimeConfig(); - let systemName = runtime.system.nameEn; - - let leftName = ''; - let rightName = ''; - - if (systemName) { - // 优先在空格处分割 - const spaceIndex = systemName.indexOf(' '); - if (spaceIndex > 0) { - leftName = systemName.substring(0, spaceIndex); - rightName = systemName.substring(spaceIndex); // 保留空格 - } else { - // 如果没有空格,从中间分割 - const mid = Math.floor(systemName.length / 2); - leftName = systemName.substring(0, mid); - rightName = systemName.substring(mid); - } - } - - return ( - <> -
-
-
- -
-
- {'logo'} { - e.currentTarget.src = '/logo.png'; - }} - /> -
-
-

- {leftName}{rightName} -

-

- {runtime.system.nameZh} -

-
-
- - - {/* HUD Navigation - Desktop Only */} -
- {[ - {id: 'servers', icon: ServerIcon, label: '设备监控', to: '/'}, - {id: 'monitors', icon: Activity, label: '服务监控', to: '/monitors'} - ].map(tab => ( - - - - ))} -
-
- - {/* Desktop Right Section */} -
-
- {currentTime.toLocaleTimeString()} - {currentTime.toLocaleDateString()} -
-
- - {/* 主题切换按钮 - Desktop */} - - - {/* 登录/管理后台按钮 - Desktop */} - {isLoggedIn ? ( - - - Admin - - ) : ( - - - Login - - )} -
- - {/* Mobile Menu Button */} - -
-
-
- - {/* Mobile Menu */} - {mobileMenuOpen && ( -
-
- {/* Mobile Navigation */} - {[ - {id: 'servers', icon: ServerIcon, label: '设备监控', to: '/'}, - {id: 'monitors', icon: Activity, label: '服务监控', to: '/monitors'} - ].map(tab => ( - setMobileMenuOpen(false)} - className={` - flex items-center gap-3 p-4 rounded-lg border transition-all - ${activeTab === tab.id - ? 'bg-blue-50 dark:bg-cyan-500/20 border-blue-500 dark:border-cyan-500/80 text-blue-600 dark:text-cyan-500' - : 'bg-slate-50/50 dark:bg-cyan-500/5 border-slate-200 dark:border-cyan-500/30 text-slate-600 dark:text-slate-400 hover:bg-blue-50 dark:hover:bg-cyan-500/10 hover:border-blue-300 dark:hover:border-cyan-500/50' - } - `} - > - - {tab.label} - - ))} - - {/* Divider */} -
- - {/* Mobile Theme Toggle Button */} - - - {/* Mobile Login/Admin Button */} - {isLoggedIn ? ( - - - 管理后台 - - ) : ( - - - 登录 - - )} -
-
- )} - - ); -}; - -/* ========================================== PublicFooter ========================================== */ - -export const PublicFooter = () => { - const currentYear = new Date().getFullYear(); - const runtime = getRuntimeConfig(); - const icpCode = runtime.system.icpCode; - - return ( -
-
-
-
-
- © {currentYear} - | - {/* GitHub 链接 */} - - Pika Monitor - - {runtime.system.version} - {/* ICP 备案号 */} - {icpCode && ( - <> - | - - {icpCode} - - - )} -
-
- 用 - - 构建 -
-
-
-
-
-
- ); -}; - -/* ========================================== TimeRangeSelector ========================================== */ - -interface CustomRange { - start: number; - end: number; -} - -interface TimeRangeSelectorProps { - value: string; - onChange: (value: string) => void; - options: readonly TimeRangeOption[]; - enableCustom?: boolean; - customRange?: CustomRange | null; - onCustomRangeApply?: (range: CustomRange) => void; - className?: string; -} - -const parseDateTimeLocal = (value: string): number | null => { - if (!value) return null; - const timestamp = new Date(value).getTime(); - if (Number.isNaN(timestamp)) { - return null; - } - return timestamp; -}; - -const toDateTimeLocal = (timestamp: number): string => { - const date = new Date(timestamp); - const offsetMs = date.getTimezoneOffset() * 60000; - return new Date(timestamp - offsetMs).toISOString().slice(0, 16); -}; - -export const TimeRangeSelector = ({ - value, - onChange, - options, - enableCustom = false, - customRange, - onCustomRangeApply, - className, - }: TimeRangeSelectorProps) => { - const [customStart, setCustomStart] = useState(''); - const [customEnd, setCustomEnd] = useState(''); - - useEffect(() => { - if (customRange?.start) { - setCustomStart(toDateTimeLocal(customRange.start)); - } - if (customRange?.end) { - setCustomEnd(toDateTimeLocal(customRange.end)); - } - }, [customRange]); - - const startMs = parseDateTimeLocal(customStart); - const endMs = parseDateTimeLocal(customEnd); - const canApply = startMs !== null && endMs !== null && startMs < endMs; - const showCustomOption = enableCustom && value === 'custom'; - - return ( -
- - {enableCustom && ( -
- - 自定义 - - setCustomStart(event.target.value)} - className="rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 focus:border-blue-400 dark:focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-500/30 px-2 py-1 text-xs font-medium" - /> - - 至 - - setCustomEnd(event.target.value)} - className="rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 focus:border-blue-400 dark:focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-500/30 px-2 py-1 text-xs font-medium" - /> - -
- )} -
- ); -}; diff --git a/portal/src/constants.ts b/portal/src/constants.ts deleted file mode 100644 index 406f60e..0000000 --- a/portal/src/constants.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type {TimeRangeOption} from './types'; - -export const LIVE_RANGE = 'live'; -export const LIVE_INITIAL_RANGE = '2m'; -export const LIVE_WINDOW_MS = 2 * 60 * 1000; - -export const SERVER_TIME_RANGE_OPTIONS: TimeRangeOption[] = [ - {label: '实时', value: LIVE_RANGE}, - {label: '15分钟', value: '15m'}, - {label: '30分钟', value: '30m'}, - {label: '1小时', value: '1h'}, - {label: '3小时', value: '3h'}, - {label: '6小时', value: '6h'}, - {label: '12小时', value: '12h'}, - {label: '1天', value: '1d'}, - {label: '3天', value: '3d'}, - {label: '7天', value: '7d'}, -]; - -export const MONITOR_TIME_RANGE_OPTIONS: TimeRangeOption[] = [ - {label: '12小时', value: '12h'}, - {label: '1天', value: '1d'}, - {label: '3天', value: '3d'}, - {label: '7天', value: '7d'}, -]; - -export const AGENT_COLORS = [ - '#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4', '#f97316', '#14b8a6', -]; - -export const INTERFACE_COLORS = [ - {upload: '#6FD598', download: '#2C70F6'}, - {upload: '#f59e0b', download: '#8b5cf6'}, - {upload: '#ec4899', download: '#06b6d4'}, - {upload: '#10b981', download: '#f97316'}, - {upload: '#14b8a6', download: '#2563eb'}, -]; - -export const TEMPERATURE_COLORS: Record = { - 'CPU': '#f97316', 'GPU': '#8b5cf6', 'DISK': '#06b6d4', 'BATTERY': '#10b981', - 'CHIPSET': '#f59e0b', 'SYSTEM': '#6366f1', 'PSU': '#ec4899', -}; - -export const ACCENT_THEMES: Record<'blue' | 'emerald' | 'purple' | 'amber', { icon: string; badge: string; highlight: string }> = { - blue: {icon: 'text-blue-400', badge: 'text-blue-400', highlight: 'text-blue-400'}, - emerald: {icon: 'text-emerald-400', badge: 'text-emerald-400', highlight: 'text-emerald-400'}, - purple: {icon: 'text-purple-400', badge: 'text-purple-400', highlight: 'text-purple-400'}, - amber: {icon: 'text-amber-400', badge: 'text-amber-400', highlight: 'text-amber-400'}, -}; diff --git a/portal/src/contexts/ColorMode.tsx b/portal/src/contexts/ColorMode.tsx deleted file mode 100644 index 676fc8d..0000000 --- a/portal/src/contexts/ColorMode.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import {createContext, useContext, useEffect, useState, type ReactNode} from 'react'; -import {getRuntimeConfig, resolveColorMode} from '../api'; -import type {ColorMode} from '../types'; - -export type ResolvedColorMode = 'light' | 'dark'; - -interface ColorModeContextValue { - colorMode: ColorMode; - resolvedColorMode: ResolvedColorMode; - setColorMode: (mode: ColorMode) => void; -} - -const ColorModeContext = createContext(undefined); - -export const ColorModeProvider = ({children}: {children: ReactNode}) => { - const [colorMode, setColorModeState] = useState(() => { - const saved = localStorage.getItem('colorMode') || localStorage.getItem('theme'); - if (saved === 'auto') return 'system'; - if (saved === 'light' || saved === 'dark' || saved === 'system') return saved; - return getRuntimeConfig().system.defaultColorMode; - }); - const [resolvedColorMode, setResolvedColorMode] = useState('dark'); - - const setColorMode = (mode: ColorMode) => { - setColorModeState(mode); - localStorage.setItem('colorMode', mode); - localStorage.removeItem('theme'); - }; - - useEffect(() => { - const apply = () => { - const resolved = resolveColorMode(colorMode); - setResolvedColorMode(resolved); - document.documentElement.classList.toggle('dark', resolved === 'dark'); - document.documentElement.dataset.colorMode = resolved; - }; - apply(); - if (colorMode !== 'system') return; - const media = window.matchMedia('(prefers-color-scheme: dark)'); - media.addEventListener('change', apply); - return () => media.removeEventListener('change', apply); - }, [colorMode]); - - return ( - - {children} - - ); -}; - -export const useColorMode = () => { - const value = useContext(ColorModeContext); - if (!value) throw new Error('useColorMode must be used within ColorModeProvider'); - return value; -}; diff --git a/portal/src/hooks/index.ts b/portal/src/hooks/index.ts deleted file mode 100644 index 2fada52..0000000 --- a/portal/src/hooks/index.ts +++ /dev/null @@ -1,128 +0,0 @@ -import {useEffect, useRef, useState} from 'react'; -import {useQuery} from '@tanstack/react-query'; -import {pika} from '../api'; -import type {Agent, LatestMetrics, MetricsAggregation, MetricsParams, MetricsResponse} from '../types'; - -interface UseMetricsQueryOptions { - agentId: string; - type: MetricsParams['type']; - range?: string; - start?: number; - end?: number; - interfaceName?: string; - aggregation?: MetricsAggregation; - refetchIntervalMs?: number; -} - -export const useAgentQuery = (agentId?: string) => { - return useQuery({ - queryKey: ['agent', agentId], - queryFn: () => pika.getAgent(agentId!), - enabled: !!agentId, - staleTime: 60000, - }); -}; - -export const useLatestMetricsQuery = (agentId?: string, intervalMs: number = 5000) => { - return useQuery({ - queryKey: ['agent', agentId, 'metrics', 'latest'], - queryFn: () => pika.getLatestMetrics(agentId!), - enabled: !!agentId, - refetchInterval: intervalMs > 0 ? intervalMs : false, - }); -}; - -export const useMetricsQuery = ({agentId, type, range, start, end, interfaceName, aggregation, refetchIntervalMs}: UseMetricsQueryOptions) => { - return useQuery({ - queryKey: ['agent', agentId, 'metrics', type, range, start, end, interfaceName, aggregation], - queryFn: () => - pika.getMetrics(agentId, { - type, - range: start !== undefined && end !== undefined ? undefined : range, - start, - end, - interface: interfaceName, - aggregation, - }), - enabled: !!agentId, - refetchInterval: refetchIntervalMs && refetchIntervalMs > 0 ? refetchIntervalMs : false, - }); -}; - -export const useNetworkInterfacesQuery = (agentId?: string) => { - return useQuery({ - queryKey: ['agent', agentId, 'network-interfaces'], - queryFn: () => pika.getNetworkInterfaces(agentId!), - enabled: !!agentId, - }); -}; - -export function useLiveBuffer( - initial: T[], - isLive: boolean, - livePoint: T | null, - windowMs: number, - resetKey?: unknown, -): T[] { - const [buffer, setBuffer] = useState([]); - const lastTsRef = useRef(0); - const seededRef = useRef(false); - - useEffect(() => { - seededRef.current = false; - lastTsRef.current = 0; - setBuffer([]); - }, [isLive, resetKey]); - - useEffect(() => { - if (!isLive) return; - if (seededRef.current) return; - if (!initial || initial.length === 0) return; - seededRef.current = true; - - const initLastTs = initial[initial.length - 1].timestamp; - setBuffer(prev => { - if (prev.length === 0) { - return initial.slice(); - } - const earliestExisting = prev[0].timestamp; - const olderHistory = initial.filter(p => p.timestamp < earliestExisting); - return olderHistory.length > 0 ? [...olderHistory, ...prev] : prev; - }); - if (initLastTs > lastTsRef.current) { - lastTsRef.current = initLastTs; - } - }, [isLive, initial, resetKey]); - - useEffect(() => { - if (!isLive || !livePoint) return; - const ts = livePoint.timestamp; - if (!Number.isFinite(ts) || ts <= 0) return; - if (ts <= lastTsRef.current) return; - lastTsRef.current = ts; - - setBuffer(prev => { - const next = prev.length === 0 ? [livePoint] : [...prev, livePoint]; - const cutoff = ts - windowMs; - let drop = 0; - while (drop < next.length && next[drop].timestamp < cutoff) drop++; - return drop > 0 ? next.slice(drop) : next; - }); - }, [livePoint, isLive, windowMs]); - - return isLive ? buffer : initial; -} - -const MOBILE_BREAKPOINT = 768; - -export function useIsMobile() { - const [isMobile, setIsMobile] = useState(undefined); - useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); - const onChange = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - mql.addEventListener('change', onChange); - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - return () => mql.removeEventListener('change', onChange); - }, []); - return !!isMobile; -} diff --git a/portal/src/index.css b/portal/src/index.css deleted file mode 100644 index 7f04aa2..0000000 --- a/portal/src/index.css +++ /dev/null @@ -1,169 +0,0 @@ -@import "tailwindcss"; -@import "tw-animate-css"; - -@custom-variant dark (&:is(.dark *)); - -/* Tailwind v4 暗色模式配置 */ -@variant dark (&:where(.dark, .dark *)); - -/* 标签过滤器滚动条样式 */ -.tag-filter-scrollbar { - scrollbar-width: thin; - scrollbar-color: rgb(203, 213, 225) transparent; - scrollbar-gutter: stable; -} - -.tag-filter-scrollbar::-webkit-scrollbar { - height: 6px; -} - -.tag-filter-scrollbar::-webkit-scrollbar-track { - background: rgb(241, 245, 249); - border-radius: 9999px; -} - -.tag-filter-scrollbar::-webkit-scrollbar-thumb { - background: rgb(203, 213, 225); - border-radius: 9999px; - transition: background-color 0.2s; -} - -.tag-filter-scrollbar::-webkit-scrollbar-thumb:hover { - background: rgb(148, 163, 184); -} - -/* 暗色模式标签过滤器滚动条 */ -.dark .tag-filter-scrollbar { - scrollbar-color: rgb(71, 85, 105) transparent; -} - -.dark .tag-filter-scrollbar::-webkit-scrollbar-track { - background: rgb(30, 41, 59); -} - -.dark .tag-filter-scrollbar::-webkit-scrollbar-thumb { - background: rgb(71, 85, 105); -} - -.dark .tag-filter-scrollbar::-webkit-scrollbar-thumb:hover { - background: rgb(100, 116, 139); -} - -@theme inline { - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --radius-2xl: calc(var(--radius) + 8px); - --radius-3xl: calc(var(--radius) + 12px); - --radius-4xl: calc(var(--radius) + 16px); - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); -} - -:root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} \ No newline at end of file diff --git a/portal/src/lib/utils.ts b/portal/src/lib/utils.ts deleted file mode 100644 index 8974321..0000000 --- a/portal/src/lib/utils.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {type ClassValue, clsx} from 'clsx'; -import {twMerge} from 'tailwind-merge'; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} - -export const formatBytes = (value: number | undefined | null, precision = 2): string => { - if (value === undefined || value === null || !Number.isFinite(value) || value <= 0) return '0 B'; - const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; - const index = Math.min(Math.floor(Math.log(value) / Math.log(1024)), units.length - 1); - return `${(value / 1024 ** index).toFixed(precision)} ${units[index]}`; -}; - -export const formatSpeed = (bytesPerSecond: number): string => { - if (!Number.isFinite(bytesPerSecond) || bytesPerSecond <= 0) return '0 B/s'; - const units = ['B/s', 'K/s', 'M/s', 'G/s', 'T/s']; - const index = Math.min(Math.floor(Math.log(bytesPerSecond) / Math.log(1024)), units.length - 1); - const value = bytesPerSecond / 1024 ** index; - const decimals = value >= 100 ? 0 : value >= 10 ? 1 : 2; - return `${value.toFixed(decimals)} ${units[index]}`; -}; - -export const formatTime = (milliseconds: number): string => { - if (!Number.isFinite(milliseconds) || milliseconds <= 0) return '0 ms'; - if (milliseconds < 1000) return `${milliseconds.toFixed(0)} ms`; - return `${(milliseconds / 1000).toFixed(2)} s`; -}; - -export const formatDateTime = (value: string | number | undefined | null): string => { - if (value === undefined || value === null || value === '') return '-'; - const date = new Date(value); - if (Number.isNaN(date.getTime())) return '-'; - const pad = (part: number) => String(part).padStart(2, '0'); - return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; -}; - -export const formatPercentValue = (value: number | undefined | null): string => { - if (value === undefined || value === null || Number.isNaN(value)) return '0.0'; - return value.toFixed(1); -}; - -export const formatUptime = (seconds: number | undefined | null): string => { - if (seconds === undefined || seconds === null) return '-'; - if (seconds <= 0) return '0 秒'; - const days = Math.floor(seconds / 86400); - const hours = Math.floor((seconds % 86400) / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - if (days > 0) return `${days} 天${hours > 0 ? ` ${hours} 小时` : ''}`; - if (hours > 0) return `${hours} 小时${minutes > 0 ? ` ${minutes} 分钟` : ''}`; - return minutes > 0 ? `${minutes} 分钟` : '不到 1 分钟'; -}; - -export const formatChartTime = (timestamp: number, timeRange: string, rangeMs?: number): string => { - const date = new Date(timestamp); - if (timeRange === 'live') { - return date.toLocaleTimeString('zh-CN', {hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false}); - } - const isLongRange = rangeMs !== undefined - ? rangeMs >= 24 * 60 * 60 * 1000 - : timeRange === '1d' || timeRange === '24h' || (timeRange.endsWith('d') && Number.parseInt(timeRange) > 1); - if (isLongRange) { - const pad = (part: number) => String(part).padStart(2, '0'); - return `${pad(date.getMonth() + 1)}/${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`; - } - return date.toLocaleTimeString('zh-CN', {hour: '2-digit', minute: '2-digit'}); -}; - -export const isExpired = (expireTime?: number) => { - return Boolean(expireTime && expireTime > 0 && expireTime - Date.now() < 30 * 24 * 60 * 60 * 1000); -}; diff --git a/portal/src/main.tsx b/portal/src/main.tsx deleted file mode 100644 index 23ad142..0000000 --- a/portal/src/main.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import {StrictMode} from 'react'; -import {createRoot} from 'react-dom/client'; -import {QueryClient, QueryClientProvider} from '@tanstack/react-query'; -import {pika} from './api'; -import type {PikaRuntimeConfig} from './types'; -import PortalApp from './App'; -import './index.css'; - -const fallbackRuntime: PikaRuntimeConfig = { - apiVersion: 'v1', - system: { - nameZh: '皮卡监控', - nameEn: 'Pika Monitor', - logo: '/api/logo', - icpCode: '', - version: '', - defaultView: 'grid', - defaultColorMode: 'system', - }, - theme: {id: 'default', version: '1.0.0'}, - features: { - serverList: true, - serverDetail: true, - monitorList: true, - monitorDetail: true, - }, -}; - -const start = async () => { - if (!window.PikaRuntime) { - try { - window.PikaRuntime = await pika.getConfig(); - } catch (error) { - window.PikaRuntime = fallbackRuntime; - console.error(error); - } - } - - const queryClient = new QueryClient({ - defaultOptions: {queries: {refetchOnWindowFocus: false, retry: 1}}, - }); - createRoot(document.getElementById('root')!).render( - - - - - , - ); -}; - -start().catch(console.error); diff --git a/portal/src/pages/MonitorDetail.tsx b/portal/src/pages/MonitorDetail.tsx deleted file mode 100644 index 41377b9..0000000 --- a/portal/src/pages/MonitorDetail.tsx +++ /dev/null @@ -1,795 +0,0 @@ -import {useEffect, useMemo, useState} from 'react'; -import {useNavigate, useParams} from 'react-router-dom'; -import {useQuery} from '@tanstack/react-query'; -import {Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts'; -import {AlertCircle, ArrowLeft, ChevronDown, ChevronUp, Clock, Globe, MapPin, RotateCcw, Server, ShieldCheck, Wifi} from 'lucide-react'; -import {pika} from '../api'; -import type {AgentMonitorStat, MetricsResponse, PublicMonitor} from '../types'; -import {AGENT_COLORS, MONITOR_TIME_RANGE_OPTIONS} from '../constants'; -import {cn, formatChartTime, formatDateTime, formatTime} from '../lib/utils'; -import {useIsMobile} from '../hooks'; -import { - Card, - ChartPlaceholder, - CustomTooltip, - EmptyState, - LoadingSpinner, - MetricItem, - StatusBadge, - TimeRangeSelector -} from '../components'; - -/* ========================================== TypeIcon ========================================== */ - -const TypeIcon = ({type}: {type: string}) => { - switch (type.toLowerCase()) { - case 'https': - return ; - case 'http': - return ; - case 'tcp': - return ; - case 'icmp': - case 'ping': - return ; - default: - return ; - } -}; - -/* ========================================== CertBadge ========================================== */ - -const CertBadge = ({expiryTime, daysLeft}: {expiryTime: number; daysLeft: number}) => { - if (!expiryTime || daysLeft === undefined) return null; - - const isExpired = daysLeft < 0; - let colorClass = "text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 border-emerald-200 dark:border-emerald-500/20"; - - if (isExpired) { - colorClass = "text-rose-600 dark:text-rose-400 bg-rose-50 dark:bg-rose-500/10 border-rose-200 dark:border-rose-500/20"; - } else if (daysLeft < 30) { - colorClass = "text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/10 border-amber-200 dark:border-amber-500/20"; - } - - return ( -
- - {isExpired ? "已过期" : `${daysLeft} 天后过期`} -
- ); -}; - -/* ========================================== MonitorHero ========================================== */ - -interface MonitorHeroProps { - monitor: PublicMonitor; - onBack: () => void; -} - -const MonitorHero = ({monitor, onBack}: MonitorHeroProps) => { - return ( - - {/* 返回按钮 */} - - - {/* 监控信息 */} -
-
-
- -
-
-
-

{monitor.name}

- -
-

- {monitor.showTargetPublic ? monitor.target : '******'} -

-
-
- - {/* 统计卡片 */} -
- - - - -
-
- - {/* 证书信息(如果存在证书数据)*/} - {monitor.certExpiryTime > 0 && ( -
- SSL 证书: -
- - - 到期时间: {formatDateTime(monitor.certExpiryTime)} - -
-
- )} -
- ); -}; - -/* ========================================== AgentStatsTable ========================================== */ - -interface AgentStatsTableProps { - monitorStats: AgentMonitorStat[]; - monitorType: string; -} - -const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProps) => { - if (monitorStats.length === 0) { - return ( -
-

暂无探针数据

-
- ); - } - - return ( - -
-

探针监控详情

-

各探针的当前状态和统计数据

-
- - {/* 移动端卡片布局 */} -
- {monitorStats.map((stat, index) => { - const color = AGENT_COLORS[index % AGENT_COLORS.length]; - return ( -
- {/* 探针名称和状态 */} -
-
- - - - {stat.agentName || stat.agentId.substring(0, 8)} - -
- -
- - {/* 响应时间和最后检测 */} -
-
- - - {formatTime(stat.responseTime)} - -
- - {formatDateTime(stat.checkedAt)} - -
- - {/* 证书信息 */} - {monitorType === 'https' && stat.certExpiryTime && ( -
-
- 证书: - -
-
- )} - - {/* 错误信息 */} - {stat.status === 'down' && stat.message && ( -
-
- - - {stat.message} - -
-
- )} -
- ); - })} -
- - {/* 桌面端表格布局 */} -
- - - - - - - - {monitorType === 'https' && ( - - )} - - - - - {monitorStats.map((stat, index) => { - const color = AGENT_COLORS[index % AGENT_COLORS.length]; - return ( - - - - - - {monitorType === 'https' && ( - - )} - - - ); - })} - -
- 探针名称 - - 状态 - - 响应时间 - - 最后检测 - - 证书信息 - - 错误信息 -
-
- -
- - - {stat.agentName || stat.agentId.substring(0, 8)} - -
-
-
- - -
- - - {formatTime(stat.responseTime)} - -
-
- {formatDateTime(stat.checkedAt)} - - {stat.certExpiryTime ? ( - - ) : ( - - - )} - - {stat.status === 'down' && stat.message ? ( -
- - - {stat.message} - -
- ) : ( - - - )} -
-
-
- ); -}; - -/* ========================================== ResponseTimeChart ========================================== */ - -/** - * 根据时间范围确定最大数据点数 - */ -const getMaxDataPoints = (timeRange: string): number => { - switch (timeRange) { - case '15m': - case '1h': - return 200; // 短时间:详细数据 - case '12h': - return 300; - case '24h': - return 400; - case '7d': - return 500; - case '30d': - return 600; - default: - return 400; - } -}; - -/** - * 生成不重复的颜色 - * 使用 HSL 色轮均匀分布,支持无限数量的探针 - */ -const generateColors = (count: number): string[] => { - const colors: string[] = []; - const hueStep = 360 / count; - - for (let i = 0; i < count; i++) { - const hue = (i * hueStep) % 360; - const saturation = 65 + (i % 3) * 10; - const lightness = 45 + (i % 2) * 10; - colors.push(`hsl(${hue}, ${saturation}%, ${lightness}%)`); - } - - return colors; -}; - -/** - * 自定义图例组件 - */ -const CustomLegend = ({onClick, selectedAgents, allAgents, colors, collapsed}: any) => { - if (!allAgents || allAgents.length === 0) return null; - - if (collapsed) return null; - - return ( -
- {allAgents.map((agent: {id: string; name: string}, index: number) => { - const isSelected = selectedAgents.has(agent.id); - const color = colors[index]; - - return ( -
onClick(agent.id)} - className="flex items-center gap-2 cursor-pointer transition-opacity" - style={{ - opacity: isSelected ? 1 : 0.4, - }} - > - - - - - {agent.name} - -
- ); - })} -
- ); -}; - -interface ResponseTimeChartProps { - monitorId: string; - monitorStats: AgentMonitorStat[]; -} - -const ResponseTimeChart = ({monitorId, monitorStats}: ResponseTimeChartProps) => { - const [selectedAgents, setSelectedAgents] = useState>(new Set()); - const [timeRange, setTimeRange] = useState('12h'); - const [customRange, setCustomRange] = useState<{start: number; end: number} | null>(null); - const [legendCollapsed, setLegendCollapsed] = useState(true); // 移动端默认收起 - const isMobile = useIsMobile(); - const customStart = timeRange === 'custom' ? customRange?.start : undefined; - const customEnd = timeRange === 'custom' ? customRange?.end : undefined; - const rangeMs = customStart !== undefined && customEnd !== undefined ? customEnd - customStart : undefined; - - // 获取历史数据 - const {data: historyData} = useQuery({ - queryKey: ['monitorHistory', monitorId, timeRange, customStart, customEnd], - queryFn: async () => { - if (!monitorId) throw new Error('Monitor ID is required'); - return pika.getMonitorHistory(monitorId, { - range: timeRange, - start: customStart, - end: customEnd, - }); - }, - refetchInterval: 30000, - enabled: !!monitorId, - }); - - // 获取所有可用的探针列表 - const availableAgents = useMemo(() => { - if (monitorStats.length === 0) return []; - return monitorStats.map(stat => ({ - id: stat.agentId, - name: stat.agentName || stat.agentId.substring(0, 8), - })); - }, [monitorStats]); - - // 初始化选中所有探针 - useEffect(() => { - if (availableAgents.length > 0 && selectedAgents.size === 0) { - setSelectedAgents(new Set(availableAgents.map(a => a.id))); - } - }, [availableAgents, selectedAgents.size]); - - // 动态生成颜色 - const colors = useMemo(() => { - return generateColors(availableAgents.length); - }, [availableAgents.length]); - - const toggleAgent = (agentId: string) => { - setSelectedAgents((current) => { - if (current.size === availableAgents.length) return new Set([agentId]); - const next = new Set(current); - if (next.has(agentId)) next.delete(agentId); - else next.add(agentId); - return next; - }); - }; - - const handleAreaClick = (data: unknown) => { - const key = (data as {dataKey?: string})?.dataKey; - if (key) toggleAgent(key.replace('agent_', '')); - }; - - // 恢复全选 - const handleSelectAll = () => { - setSelectedAgents(new Set(availableAgents.map(a => a.id))); - }; - - // 是否有探针未选中 - const hasUnselected = selectedAgents.size < availableAgents.length; - - // 切换图例显示/隐藏(仅移动端) - const toggleLegend = () => setLegendCollapsed((collapsed) => !collapsed); - - // 生成图表数据 - 使用统一时间网格对齐 - const chartData = useMemo(() => { - if (!historyData?.series) return []; - - const seriesList = historyData.series.filter(s => s.name === 'response_time'); - if (seriesList.length === 0) return []; - - // 收集所有选中探针的数据 - const selectedSeriesData: Array<{key: string; data: Array<{timestamp: number; value: number}>}> = []; - - seriesList.forEach((s) => { - const agentId = s.labels?.agent_id || 'unknown'; - if (!selectedAgents.has(agentId)) return; - if (!s.data || s.data.length === 0) return; - - selectedSeriesData.push({ - key: `agent_${agentId}`, - data: [...s.data].sort((a, b) => a.timestamp - b.timestamp) - }); - }); - - if (selectedSeriesData.length === 0) return []; - - // 确定全局时间范围 - let minTime = Infinity, maxTime = -Infinity; - selectedSeriesData.forEach(s => { - minTime = Math.min(minTime, s.data[0].timestamp); - maxTime = Math.max(maxTime, s.data[s.data.length - 1].timestamp); - }); - - if (minTime >= maxTime) return []; - - // 生成均匀的目标时间点 - const maxPoints = getMaxDataPoints(timeRange); - const timeStep = (maxTime - minTime) / (maxPoints - 1); - const targetTimestamps: number[] = []; - for (let i = 0; i < maxPoints; i++) { - targetTimestamps.push(minTime + i * timeStep); - } - - // 线性插值函数 - const interpolate = (data: Array<{timestamp: number; value: number}>, targetTime: number): number | null => { - if (data.length === 0) return null; - if (data.length === 1) return data[0].timestamp === targetTime ? data[0].value : null; - - // 超出范围不插值(防止产生虚假连线) - if (targetTime < data[0].timestamp || targetTime > data[data.length - 1].timestamp) { - // 如果距离最近的点足够近(比如小于两个采样间隔),可以考虑保留,否则返回null - return null; - } - - // 二分查找 - let left = 0, right = data.length - 1; - while (right - left > 1) { - const mid = Math.floor((left + right) / 2); - if (data[mid].timestamp <= targetTime) { - left = mid; - } else { - right = mid; - } - } - - const leftPoint = data[left]; - const rightPoint = data[right]; - const ratio = (targetTime - leftPoint.timestamp) / (rightPoint.timestamp - leftPoint.timestamp); - return leftPoint.value + ratio * (rightPoint.value - leftPoint.value); - }; - - // 对齐所有探针数据 - return targetTimestamps.map(timestamp => { - const dataPoint: any = {timestamp}; - selectedSeriesData.forEach(s => { - const value = interpolate(s.data, timestamp); - if (value !== null) { - dataPoint[s.key] = Number(value.toFixed(2)); - } - }); - return dataPoint; - }); - }, [historyData, selectedAgents, timeRange, customStart, customEnd]); - - return ( - -
-
-

响应时间趋势

-

监控各探针的响应时间变化

-
-
- { - setCustomRange(range); - }} - /> -
-
- - {/* 使用提示和恢复按钮 */} - {availableAgents.length > 0 && ( -
-
- 💡 点击图表线条或图例切换显示 -
- {hasUnselected && ( - - )} -
- )} - - {chartData.length > 0 ? ( -
- - - - {Array.from(selectedAgents).map((agentId, index) => { - const originalIndex = availableAgents.findIndex(a => a.id === agentId); - const agentKey = `agent_${agentId}`; - return ( - - - - - ); - })} - - - formatChartTime(Number(value), timeRange, rangeMs)} - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - stroke="currentColor" - tickLine={false} - axisLine={false} - angle={-15} - textAnchor="end" - /> - `${value}ms`} - /> - } - wrapperStyle={{zIndex: 9999}} - /> - {Array.from(selectedAgents).map((agentId) => { - const originalIndex = availableAgents.findIndex(a => a.id === agentId); - const agentKey = `agent_${agentId}`; - const agent = availableAgents.find(a => a.id === agentId); - return ( - - ); - })} - - - - {/* 桌面端:直接显示图例 */} - {!isMobile && availableAgents.length > 0 && ( - - )} - - {/* 移动端:可折叠图例 */} - {isMobile && availableAgents.length > 0 && ( -
- - -
- )} -
- ) : ( - - )} -
- ); -}; - -/* ========================================== MonitorDetail ========================================== */ - -/** - * 监控详情页面 - * 显示监控的详细信息、响应时间趋势和各探针统计 - */ -const MonitorDetail = () => { - const navigate = useNavigate(); - const {id} = useParams<{id: string}>(); - - // 获取监控详情(聚合数据) - const {data: monitorDetail, isLoading} = useQuery({ - queryKey: ['monitorDetail', id], - queryFn: async () => { - if (!id) throw new Error('Monitor ID is required'); - return pika.getMonitorStats(id); - }, - refetchInterval: 30000, - enabled: !!id, - }); - - // 获取各探针的统计数据 - const {data: monitorStats = []} = useQuery({ - queryKey: ['monitorAgentStats', id], - queryFn: async () => { - if (!id) return []; - return pika.getMonitorAgents(id); - }, - refetchInterval: 30000, - enabled: !!id, - }); - - if (isLoading) { - return ; - } - - if (!monitorDetail) { - return ; - } - - return ( -
-
- {/* 头部区域 */} - navigate('/monitors')} - /> - - {/* 主内容区 */} -
- {/* 响应时间趋势图表 */} - - - {/* 各探针详细数据 */} - -
-
-
- ); -}; - -export default MonitorDetail; diff --git a/portal/src/pages/MonitorList.tsx b/portal/src/pages/MonitorList.tsx deleted file mode 100644 index c258027..0000000 --- a/portal/src/pages/MonitorList.tsx +++ /dev/null @@ -1,431 +0,0 @@ -import {useEffect, useMemo, useState} from 'react'; -import {Link} from 'react-router-dom'; -import {useQuery} from '@tanstack/react-query'; -import {AlertTriangle, BarChart3, CheckCircle2, Globe, Loader2, Maximize2, Search, Server, Shield, ShieldCheck, Wifi, Zap} from 'lucide-react'; -import {Area, AreaChart, ResponsiveContainer} from 'recharts'; -import {pika} from '../api'; -import type {MetricsResponse, PublicMonitor} from '../types'; -import {cn, formatDateTime} from '../lib/utils'; -import {Card, StatBlock, StatusBadge} from '../components'; - -/* ========================================== TypeIcon ========================================== */ - -const TypeIcon = ({type}: {type: string}) => { - switch (type.toLowerCase()) { - case 'https': - return ; - case 'http': - return ; - case 'tcp': - return ; - case 'icmp': - case 'ping': - return ; - default: - return ; - } -}; - -/* ========================================== CertBadge ========================================== */ - -const CertBadge = ({expiryTime, daysLeft}: {expiryTime: number; daysLeft: number}) => { - if (!expiryTime || daysLeft === undefined) return null; - - const isExpired = daysLeft < 0; - let colorClass = "text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 border-emerald-200 dark:border-emerald-500/20"; - - if (isExpired) { - colorClass = "text-rose-600 dark:text-rose-400 bg-rose-50 dark:bg-rose-500/10 border-rose-200 dark:border-rose-500/20"; - } else if (daysLeft < 30) { - colorClass = "text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/10 border-amber-200 dark:border-amber-500/20"; - } - - return ( -
- - {isExpired ? "已过期" : `${daysLeft} 天后过期`} -
- ); -}; - -/* ========================================== MonitorCard ========================================== */ - -export type DisplayMode = 'avg' | 'max'; - -const MiniChart = ({data, lastValue, id}: { - data: Array<{timestamp: number; value: number}>; - lastValue?: number; - id: string; -}) => { - const chartData = useMemo(() => [...data].sort((a, b) => a.timestamp - b.timestamp), [data]); - if (chartData.length === 0) { - return
暂无数据
; - } - - const color = lastValue && lastValue <= 200 ? '#22d3ee' : '#fbbf24'; - return ( -
- - - - - - - - - - - - - - - - - - -
- ); -}; - -const MonitorCard = ({monitor, displayMode}: { - monitor: PublicMonitor; - displayMode: DisplayMode; -}) => { - // 为每个监控卡片查询历史数据 - const {data: historyData} = useQuery({ - queryKey: ['monitorHistory', monitor.id, '12h'], // 对应后端 60 秒步长 - queryFn: async () => { - return pika.getMonitorHistory(monitor.id, {range: '1h'}); - }, - refetchInterval: 60000, - staleTime: 30000, - }); - - // 转换时序数据为图表数据 - 使用统一格点对该对齐多探针数据 - const chartData = useMemo(() => { - if (!historyData?.series || historyData.series.length === 0) { - return []; - } - - const validSeries = historyData.series.filter(s => s.data && s.data.length > 0); - if (validSeries.length === 0) return []; - - // 确定全局时间范围 - let minTime = Infinity, maxTime = -Infinity; - validSeries.forEach(s => { - minTime = Math.min(minTime, s.data![0].timestamp); - maxTime = Math.max(maxTime, s.data![s.data!.length - 1].timestamp); - }); - - if (minTime >= maxTime) return []; - - // 定义目标采集点 (1小时数据,建议 60 个采集点) - const maxPoints = 60; - const timeStep = (maxTime - minTime) / (maxPoints - 1); - const targetTimestamps: number[] = []; - for (let i = 0; i < maxPoints; i++) { - targetTimestamps.push(minTime + i * timeStep); - } - - // 线性插值函数 - const interpolate = (data: Array<{ timestamp: number; value: number }>, targetTime: number): number | null => { - if (data.length === 0) return null; - if (data.length === 1) return data[0].timestamp === targetTime ? data[0].value : null; - if (targetTime < data[0].timestamp || targetTime > data[data.length - 1].timestamp) return null; - - let left = 0, right = data.length - 1; - while (right - left > 1) { - const mid = Math.floor((left + right) / 2); - if (data[mid].timestamp <= targetTime) left = mid; - else right = mid; - } - const leftPoint = data[left]; - const rightPoint = data[right]; - const ratio = (targetTime - leftPoint.timestamp) / (rightPoint.timestamp - leftPoint.timestamp); - return leftPoint.value + ratio * (rightPoint.value - leftPoint.value); - }; - - // 对每个目标时间点,计算所有探针的聚合值 - return targetTimestamps.map(timestamp => { - const values: number[] = []; - validSeries.forEach(s => { - const val = interpolate(s.data!, timestamp); - if (val !== null) values.push(val); - }); - - if (values.length === 0) return { timestamp, value: 0 }; - - return { - timestamp, - value: displayMode === 'avg' - ? Math.round(values.reduce((a, b) => a + b, 0) / values.length) - : Math.max(...values), - }; - }); - }, [historyData, displayMode]); - - const displayValue = displayMode === 'avg' ? monitor.responseTime : monitor.responseTimeMax; - const displayLabel = displayMode === 'avg' ? '平均延迟' : '最差节点延迟'; - - return ( - - {/* 头部 */} -
-
-
- -
-
-

- {monitor.name} -

-
- {monitor.target} -
-
-
-
- -
-
- - {/* 指标信息 */} -
-
-

- {displayLabel} - {monitor.agentCount > 0 && ( - - {monitor.agentCount} 节点 - - )} -

-
200 ? 'text-amber-600 dark:text-amber-400 drop-shadow-[0_0_8px_rgba(251,191,36,0.3)] dark:drop-shadow-[0_0_8px_rgba(251,191,36,0.5)]' : 'text-slate-800 dark:text-white drop-shadow-none dark:drop-shadow-[0_0_8px_rgba(34,211,238,0.5)]'}`}> - {displayValue}ms -
-
-
- {monitor.type === 'https' && monitor.certExpiryTime ? ( - <> -

SSL 证书

- - - ) : ( - <> -

上次检测

-

- {formatDateTime(monitor.lastCheckTime)} -

- - )} -
-
- - {/* 迷你走势图 */} - -
- ); -}; - -/* ========================================== MonitorList 本地组件 ========================================== */ - -const MonitorListSpinner = () => ( -
-
- - 加载监控数据中... -
-
-); - -const MonitorListEmpty = () => ( -
- -

暂无监控数据

-

请先在管理后台添加监控任务

-
-); - - -interface Stats { - total: number; - online: number; - issues: number; - avgLatency: number; -} - -/* ========================================== MonitorList ========================================== */ - -const MonitorList = () => { - const [searchKeyword, setSearchKeyword] = useState(''); - const [displayMode, setDisplayMode] = useState('max'); - - const {data: monitors = [], isLoading} = useQuery({ - queryKey: ['publicMonitors'], - queryFn: () => pika.listMonitors(), - refetchInterval: 30000, - }); - - let [stats, setStats] = useState(); - - // 过滤和搜索 - const filteredMonitors = useMemo(() => { - let result = [...monitors]; - - // 搜索过滤 - if (searchKeyword.trim()) { - const keyword = searchKeyword.toLowerCase(); - result = result.filter(m => - m.name.toLowerCase().includes(keyword) || - m.target.toLowerCase().includes(keyword) - ); - } - - return result.sort((a, b) => Number(a.status !== 'up') - Number(b.status !== 'up')); - }, [monitors, searchKeyword]); - - // 统计信息 - const calculateStats = (monitors: PublicMonitor[]) => { - const total = monitors.length; - const online = monitors.filter(m => m.status === 'up').length; - const issues = total - online; - const avgLatency = total > 0 - ? Math.round(monitors.reduce((acc, curr) => acc + curr.responseTime, 0) / total) - : 0; - return {total, online, issues, avgLatency}; - } - - useEffect(() => { - let stats = calculateStats(monitors); - setStats(stats); - }, [monitors]); - - if (isLoading) { - return ( -
- -
- ); - } - - return ( -
- {/* 统计卡片 */} -
- - - 0} - /> - -
- - {/* 过滤和搜索 */} -
-
- {/* 显示模式切换 */} -
- 卡片指标: - - -
-
- - {/* 搜索框 */} -
-
-
- - setSearchKeyword(e.target.value)} - className="w-full bg-transparent border-none text-xs text-gray-800 dark:text-cyan-100 p-2.5 focus:ring-0 placeholder-gray-400 dark:placeholder-cyan-600 font-mono focus:outline-none" - /> -
-
-
- - {/* 监控卡片列表 */} - {filteredMonitors.length === 0 ? ( - - ) : ( -
- {filteredMonitors.map(monitor => ( - - - - ))} -
- )} -
- ); -}; - -export default MonitorList; diff --git a/portal/src/pages/PublicLayout.tsx b/portal/src/pages/PublicLayout.tsx deleted file mode 100644 index 754cbb4..0000000 --- a/portal/src/pages/PublicLayout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {Outlet} from 'react-router-dom'; -import {PublicHeader, PublicFooter} from '../components'; - -const PublicLayout = () => { - return ( -
- {/* 背景网格效果 */} -
- {/* 暗色模式网格 */} -
- {/* 顶部发光效果 */} -
- - -
-
- -
- -
-
- ); -}; - -export default PublicLayout; diff --git a/portal/src/pages/ServerDetail.tsx b/portal/src/pages/ServerDetail.tsx deleted file mode 100644 index ca64ea6..0000000 --- a/portal/src/pages/ServerDetail.tsx +++ /dev/null @@ -1,2057 +0,0 @@ -import {memo, type ReactNode, useEffect, useMemo, useState} from 'react'; -import {useNavigate, useParams} from 'react-router-dom'; -import {Area, AreaChart, CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts'; -import { - Activity, - ArrowLeft, - ChevronDown, - ChevronUp, - Cpu, - HardDrive, - MemoryStick, - Network, - RotateCcw, - Thermometer, - Zap -} from 'lucide-react'; -import type {LucideIcon} from 'lucide-react'; -import type {Agent, LatestMetrics} from '../types'; -import { - INTERFACE_COLORS, - LIVE_INITIAL_RANGE, - LIVE_RANGE, - LIVE_WINDOW_MS, - SERVER_TIME_RANGE_OPTIONS, - TEMPERATURE_COLORS, -} from '../constants'; -import { - cn, - formatBytes, - formatChartTime, - formatDateTime, - formatPercentValue, - formatUptime, -} from '../lib/utils'; -import { - useAgentQuery, - useLatestMetricsQuery, - useLiveBuffer, - useMetricsQuery, - useIsMobile, - useNetworkInterfacesQuery, -} from '../hooks'; -import { - Card, - ChartPlaceholder, - CustomTooltip, - EmptyState, - LoadingSpinner, - MetricItem, - StatusBadge, - TimeRangeSelector -} from '../components'; - -/* ========================================== 共享工具 ========================================== */ - -const toMB = (bytes: number) => Number((bytes / 1024 / 1024).toFixed(2)); - -/* ========================================== ChartContainer ========================================== */ - -interface ChartContainerProps { - title: string; - icon: LucideIcon; - children: ReactNode; - action?: ReactNode; -} - -const ChartContainer = ({title, icon: Icon, children, action}: ChartContainerProps) => { - return ( -
-
-

- - - - {title} -

- {action} -
- {children} -
- ); -}; - -/* ========================================== ServerHero ========================================== */ - -interface ServerHeroProps { - agent: Agent; - latestMetrics: LatestMetrics | null; - onBack: () => void; -} - -const ServerHero = ({agent, latestMetrics, onBack}: ServerHeroProps) => { - const displayName = agent?.name?.trim() ? agent.name : '未命名探针'; - const isOnline = agent?.status === 1; - const statusDotStyles = isOnline ? 'bg-emerald-500' : 'bg-rose-500'; - const statusText = isOnline ? '在线' : '离线'; - - const platformDisplay = latestMetrics?.host?.platform - ? `${latestMetrics.host.platform} ${latestMetrics.host.platformVersion || ''}`.trim() - : agent?.os || '-'; - const architectureDisplay = latestMetrics?.host?.kernelArch || agent?.arch || '-'; - const uptimeDisplay = formatUptime(latestMetrics?.host?.uptime); - const lastSeenDisplay = agent ? formatDateTime(agent.lastSeenAt) : '-'; - - const networkSummary = latestMetrics?.network - ? `${formatBytes(latestMetrics.network.totalBytesSentTotal)} ↑ / ${formatBytes( - latestMetrics.network.totalBytesRecvTotal, - )} ↓` - : '—'; - - const heroStats = [ - {label: '运行系统', value: platformDisplay || '-'}, - {label: '硬件架构', value: architectureDisplay || '-'}, - {label: '系统进程', value: latestMetrics?.host?.procs || '-'}, - {label: '运行时长', value: uptimeDisplay}, - ]; - - return ( - -
-
-
- -
-
-
-

{displayName}

- -
-

- {[agent.hostname].filter(Boolean).join(' · ') || '-'} -

-
-
-
- -
- {heroStats.map((stat) => ( - - ))} -
-
-
- 探针 ID:{agent.id} - - 版本:{agent.version || '-'} - - 网络累计:{networkSummary} -
-
-
- ); -}; - -/* ========================================== SystemInfoSection ========================================== */ - -interface SystemInfoSectionProps { - agent: Agent; - latestMetrics: LatestMetrics | null; -} - -const InfoGrid = ({items}: {items: Array<{label: string; value: ReactNode}>}) => ( -
- {items.map((item) => ( -
-
{item.label}
-
{item.value}
-
- ))} -
-); - -type SnapshotCardData = { - key: string; - icon: LucideIcon; - title: string; - usagePercent: string; - accent: 'blue' | 'emerald' | 'purple' | 'amber'; - metrics: Array<{label: string; value: ReactNode}>; -}; - -const snapshotColors = { - blue: 'text-blue-400', - emerald: 'text-emerald-400', - purple: 'text-purple-400', - amber: 'text-amber-400', -}; - -const SnapshotGrid = ({cards}: {cards: SnapshotCardData[]}) => ( -
- {cards.map((card) => ( -
-
-
- - - -

{card.title}

-
- {card.usagePercent} -
-
- {card.metrics.map((metric) => ( -
- {metric.label} - {metric.value} -
- ))} -
-
- ))} -
-); - -const SystemInfoSection = ({agent, latestMetrics}: SystemInfoSectionProps) => { - // 环境信息 - const platformDisplay = latestMetrics?.host?.platform - ? `${latestMetrics.host.platform} ${latestMetrics.host.platformVersion || ''}`.trim() - : agent?.os || '-'; - const architectureDisplay = latestMetrics?.host?.kernelArch || agent?.arch || '-'; - - const environmentInfo = [ - {label: '操作系统', value: platformDisplay || '-'}, - {label: '内核版本', value: latestMetrics?.host?.kernelVersion || '-'}, - {label: '硬件架构', value: architectureDisplay || '-'}, - {label: 'CPU 型号', value: latestMetrics?.cpu?.modelName || '-'}, - {label: '逻辑核心', value: latestMetrics?.cpu?.logicalCores ?? '-'}, - {label: '物理核心', value: latestMetrics?.cpu?.physicalCores ?? '-'}, - ]; - - // 状态信息 - const uptimeDisplay = formatUptime(latestMetrics?.host?.uptime); - const bootTimeDisplay = latestMetrics?.host?.bootTime - ? formatDateTime(latestMetrics.host.bootTime * 1000) - : '-'; - const lastSeenDisplay = agent ? formatDateTime(agent.lastSeenAt) : '-'; - - const networkSummary = latestMetrics?.network - ? `${formatBytes(latestMetrics.network.totalBytesSentTotal)} ↑ / ${formatBytes( - latestMetrics.network.totalBytesRecvTotal, - )} ↓` - : '—'; - - const statusInfo = [ - {label: '启动时间', value: bootTimeDisplay}, - {label: '运行时间', value: uptimeDisplay}, - {label: '最近心跳', value: lastSeenDisplay}, - {label: '进程数', value: latestMetrics?.host?.procs ?? '-'}, - {label: '网络累计', value: networkSummary}, - {label: 'Load', value: `${latestMetrics?.host?.load1?.toFixed(2)} / ${latestMetrics?.host?.load5?.toFixed(2)} / ${latestMetrics?.host?.load15?.toFixed(2)}`}, - ]; - - // 快照卡片 - const snapshotCards: SnapshotCardData[] = []; - - if (latestMetrics) { - snapshotCards.push({ - key: 'cpu', - icon: Cpu, - title: 'CPU 使用', - usagePercent: `${formatPercentValue(latestMetrics.cpu?.usagePercent)}%`, - accent: 'blue', - metrics: [ - {label: '当前使用', value: `${formatPercentValue(latestMetrics.cpu?.usagePercent)}%`}, - ], - }); - - snapshotCards.push({ - key: 'memory', - icon: MemoryStick, - title: '内存使用', - usagePercent: `${formatPercentValue(latestMetrics.memory?.usagePercent)}%`, - accent: 'emerald', - metrics: [ - { - label: '已用 / 总量', - value: `${formatBytes(latestMetrics.memory?.used)} / ${formatBytes(latestMetrics.memory?.total)}` - }, - { - label: 'Swap 已用', - value: `${formatBytes(latestMetrics.memory?.swapUsed)} / ${formatBytes(latestMetrics.memory?.swapTotal)}` - }, - ], - }); - - snapshotCards.push({ - key: 'disk', - icon: HardDrive, - title: '磁盘使用', - usagePercent: latestMetrics.disk - ? `${formatPercentValue(latestMetrics.disk.usagePercent)}%` - : '—', - accent: 'purple', - metrics: [ - { - label: '已用 / 总量', - value: `${formatBytes(latestMetrics.disk?.used, 1)} / ${formatBytes(latestMetrics.disk?.total, 1)}` - }, - {label: '磁盘数量', value: latestMetrics.disk?.totalDisks ?? '-'}, - ], - }); - - // 网络流量卡片 - 整合流量统计信息 - const networkMetrics = [ - { - label: '上行 / 下行', - value: `${formatBytes(latestMetrics.network?.totalBytesSentRate, 1)}/s ↑ / ${formatBytes( - latestMetrics.network?.totalBytesRecvRate, 1, - )}/s ↓`, - }, - { - label: '网络累计', - value: `${formatBytes(latestMetrics.network?.totalBytesSentTotal, 1)} ↑ / ${formatBytes( - latestMetrics.network?.totalBytesRecvTotal, 1, - )} ↓`, - }, - ]; - - // 如果配置了流量限额,添加流量统计信息到网络卡片 - if (agent?.trafficStats?.enabled && agent.trafficStats.limit > 0) { - const trafficUsedPercent = (agent.trafficStats.used / agent.trafficStats.limit) * 100; - - networkMetrics.push({ - label: '流量限额', - value: `${formatBytes(agent.trafficStats.used, 1)} / ${formatBytes(agent.trafficStats.limit, 1)} (${formatPercentValue(trafficUsedPercent)}%)`, - }); - - if (agent.trafficStats.resetDay > 0) { - networkMetrics.push({ - label: '重置日期', - value: `每月${agent.trafficStats.resetDay}号`, - }); - } - } - - snapshotCards.push({ - key: 'network', - icon: Network, - title: '网络流量', - usagePercent: latestMetrics.network - ? `${formatBytes(latestMetrics.network.totalBytesSentRate)}/s` - : '—', - accent: 'amber', - metrics: networkMetrics, - }); - } - - return ( -
-
-
- -

运行环境

-

来自最近一次探针上报的硬件与系统信息

-
- -
-
- -

运行状态

-

关键时间与网络指标,帮助快速判断主机健康状况

-
- -
-
-
- {snapshotCards.length > 0 && ( - -

- 资源快照 -

- -
- )} -
-
- ); -}; - -/* ========================================== ServerDetailSections ========================================== */ - -const NetworkAddressSection = ({ipv4, ipv6, deviceIpInterfaces}: { - ipv4?: string; - ipv6?: string; - deviceIpInterfaces: Array<{name: string; addrs: string[]}>; -}) => { - if (!ipv4 && !ipv6 && deviceIpInterfaces.length === 0) return null; - - return ( - -
- {(ipv4 || ipv6) && ( -
-
公网地址
-
- {[["IPv4", ipv4], ["IPv6", ipv6]].map(([label, value]) => ( -
-
{label}
-
{value || '-'}
-
- ))} -
-
- )} - - {(ipv4 || ipv6) && deviceIpInterfaces.length > 0 &&
} - - {deviceIpInterfaces.length > 0 && ( -
-
网卡地址
-
- {deviceIpInterfaces.map((networkInterface) => ( -
-
{networkInterface.name}
-
- {networkInterface.addrs.map((address) => ( - - {address} - - ))} -
-
- ))} -
-
- )} -
- - ); -}; - -const NetworkConnectionSection = ({latestMetrics}: {latestMetrics: LatestMetrics | null}) => { - const metrics = latestMetrics?.networkConnection; - if (!metrics) return null; - const items = [ - {label: 'Total', value: metrics.total, color: 'text-slate-800 dark:text-cyan-100'}, - {label: 'ESTABLISHED', value: metrics.established, color: 'text-emerald-600 dark:text-emerald-400'}, - {label: 'TIME_WAIT', value: metrics.timeWait, color: 'text-amber-600 dark:text-amber-400'}, - {label: 'LISTEN', value: metrics.listen, color: 'text-blue-600 dark:text-blue-400'}, - {label: 'CLOSE_WAIT', value: metrics.closeWait, color: 'text-rose-600 dark:text-rose-400'}, - { - label: 'OTHER', - value: metrics.synSent + metrics.synRecv + metrics.finWait1 + metrics.finWait2 + metrics.close + metrics.lastAck + metrics.closing, - color: 'text-gray-700 dark:text-cyan-500', - }, - ]; - - return ( - -
- {items.map((item) => ( -
-
{item.label}
-
{item.value}
-
- ))} -
-
- ); -}; - -const GpuMonitorSection = ({latestMetrics}: {latestMetrics: LatestMetrics | null}) => { - if (!latestMetrics?.gpu?.length) return null; - - return ( - -
- {latestMetrics.gpu.map((gpu) => ( -
-
-
- -
-

GPU {gpu.index}

-

{gpu.name}

-
-
- {gpu.utilization?.toFixed(1) ?? 0}% -
-
- {[ - ['温度', `${gpu.temperature?.toFixed(1)}°C`], - ['显存', `${formatBytes(gpu.memoryUsed)} / ${formatBytes(gpu.memoryTotal)}`], - ['功耗', `${gpu.powerUsage?.toFixed(1)}W`], - ['风扇转速', `${gpu.fanSpeed?.toFixed(0)}%`], - ].map(([label, value]) => ( -
- {label} - {value} -
- ))} -
-
- ))} -
-
- ); -}; - -const TemperatureMonitorSection = ({latestMetrics}: {latestMetrics: LatestMetrics | null}) => { - if (!latestMetrics?.temperature?.length) return null; - - return ( - -
- {[...latestMetrics.temperature].sort((a, b) => a.sensorKey.localeCompare(b.sensorKey)).map((temperature) => ( -
-
- -

{temperature.type}

-
-

{temperature.temperature.toFixed(1)}°C

-
- ))} -
-
- ); -}; - -/* ========================================== CpuChart ========================================== */ - -interface ChartPropsBase { - agentId: string; - timeRange: string; - start?: number; - end?: number; - isLive?: boolean; - latestMetrics?: LatestMetrics | null; -} - -interface CpuChartProps extends ChartPropsBase { -} - -interface CpuPoint { - timestamp: number; - usage: number; -} - -const CpuChart = ({agentId, timeRange, start, end, isLive, latestMetrics}: CpuChartProps) => { - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - // 数据查询 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'cpu', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - }); - - // 历史数据 - const initialData = useMemo(() => { - const cpuSeries = metricsResponse?.series?.find(s => s.name === 'usage'); - if (!cpuSeries) return []; - return cpuSeries.data.map((point) => ({ - usage: Number(point.value.toFixed(2)), - timestamp: point.timestamp, - })); - }, [metricsResponse]); - - // 实时点 - const livePoint = useMemo(() => { - if (!isLive || !latestMetrics?.cpu || !latestMetrics.timestamp) return null; - const usage = latestMetrics.cpu.usagePercent; - if (typeof usage !== 'number' || !Number.isFinite(usage)) return null; - return {timestamp: latestMetrics.timestamp, usage: Number(usage.toFixed(2))}; - }, [isLive, latestMetrics]); - - const chartData = useLiveBuffer(initialData, !!isLive, livePoint, LIVE_WINDOW_MS, agentId); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - return ( - - {chartData.length > 0 ? ( - - - - - - - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - /> - `${value}%`} - /> - }/> - - - - ) : ( - - )} - - ); -}; - -/* ========================================== MemoryChart ========================================== */ - -interface MemoryPoint { - timestamp: number; - usage: number; -} - -const MemoryChart = ({agentId, timeRange, start, end, isLive, latestMetrics}: ChartPropsBase) => { - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - // 数据查询 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'memory', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - }); - - // 历史数据 - const initialData = useMemo(() => { - const memorySeries = metricsResponse?.series?.find(s => s.name === 'usage'); - if (!memorySeries) return []; - return memorySeries.data.map((point) => ({ - usage: Number(point.value.toFixed(2)), - timestamp: point.timestamp, - })); - }, [metricsResponse]); - - // 实时点 - const livePoint = useMemo(() => { - if (!isLive || !latestMetrics?.memory || !latestMetrics.timestamp) return null; - const usage = latestMetrics.memory.usagePercent; - if (typeof usage !== 'number' || !Number.isFinite(usage)) return null; - return {timestamp: latestMetrics.timestamp, usage: Number(usage.toFixed(2))}; - }, [isLive, latestMetrics]); - - const chartData = useLiveBuffer(initialData, !!isLive, livePoint, LIVE_WINDOW_MS, agentId); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - return ( - - {chartData.length > 0 ? ( - - - - - - - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - /> - `${value}%`} - /> - }/> - - - - ) : ( - - )} - - ); -}; - -/* ========================================== DiskIOChart ========================================== */ - -interface DiskIOPoint { - timestamp: number; - read: number; - write: number; -} - -const DiskIOChart = ({agentId, timeRange, start, end, isLive, latestMetrics}: ChartPropsBase) => { - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - // 数据查询 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'disk_io', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - }); - - // 历史数据 - const initialData = useMemo(() => { - if (!metricsResponse?.series || metricsResponse.series.length === 0) return []; - - const readSeries = metricsResponse.series.find(s => s.name === 'read'); - const writeSeries = metricsResponse.series.find(s => s.name === 'write'); - - if (!readSeries || !writeSeries) return []; - - const timeMap = new Map(); - - readSeries.data.forEach(point => { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - read: toMB(point.value), - write: 0, - }); - }); - - writeSeries.data.forEach(point => { - const existing = timeMap.get(point.timestamp); - if (existing) { - existing.write = toMB(point.value); - } else { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - read: 0, - write: toMB(point.value), - }); - } - }); - - return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp); - }, [metricsResponse]); - - // 实时点:用 latestMetrics.timestamp 作为同批次时间锚 - const livePoint = useMemo(() => { - if (!isLive || !latestMetrics?.diskIO || !latestMetrics.timestamp) return null; - return { - timestamp: latestMetrics.timestamp, - read: toMB(latestMetrics.diskIO.totalReadBytesRate), - write: toMB(latestMetrics.diskIO.totalWriteBytesRate), - }; - }, [isLive, latestMetrics]); - - const chartData = useLiveBuffer(initialData, !!isLive, livePoint, LIVE_WINDOW_MS, agentId); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - return ( - - {chartData.length > 0 ? ( - - - - - - - - - - - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - height={45} - /> - `${value} MB`} - /> - }/> - - - - - - ) : ( - - )} - - ); -}; - -/* ========================================== NetworkChart ========================================== */ - -interface NetworkPoint { - timestamp: number; - upload: number; - download: number; -} - -const NetworkChart = ({agentId, timeRange, start, end, isLive, latestMetrics}: ChartPropsBase) => { - const [selectedInterface, setSelectedInterface] = useState('all'); - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - - // 查询网卡列表 - const {data: interfacesData} = useNetworkInterfacesQuery(agentId); - const availableInterfaces = interfacesData?.interfaces || []; - - // 当网卡列表变化时,验证选中的网卡 - useEffect(() => { - if (selectedInterface !== 'all' && availableInterfaces.length > 0) { - if (!availableInterfaces.includes(selectedInterface)) { - setSelectedInterface('all'); - } - } - }, [availableInterfaces, selectedInterface]); - - // 查询网络数据 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'network', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - interfaceName: selectedInterface !== 'all' ? selectedInterface : undefined, - }); - - // 历史数据 - const initialData = useMemo(() => { - if (!metricsResponse?.series || metricsResponse.series.length === 0) return []; - - const uploadSeries = metricsResponse.series.find(s => s.name === 'upload'); - const downloadSeries = metricsResponse.series.find(s => s.name === 'download'); - - if (!uploadSeries || !downloadSeries) return []; - - const timeMap = new Map(); - - uploadSeries.data.forEach(point => { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - upload: toMB(point.value), - download: 0, - }); - }); - - downloadSeries.data.forEach(point => { - const existing = timeMap.get(point.timestamp); - if (existing) { - existing.download = toMB(point.value); - } else { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - upload: 0, - download: toMB(point.value), - }); - } - }); - - return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp); - }, [metricsResponse]); - - // 实时点:用 latestMetrics.timestamp 作为采集批次时间锚点 - const livePoint = useMemo(() => { - if (!isLive || !latestMetrics?.timestamp) return null; - - let sentRate = 0; - let recvRate = 0; - if (selectedInterface === 'all') { - const summary = latestMetrics.network; - if (!summary) return null; - sentRate = summary.totalBytesSentRate; - recvRate = summary.totalBytesRecvRate; - } else { - const iface = latestMetrics.networkInterfaces?.find(i => i.interface === selectedInterface); - if (!iface) return null; - sentRate = iface.bytesSentRate; - recvRate = iface.bytesRecvRate; - } - return { - timestamp: latestMetrics.timestamp, - upload: toMB(sentRate), - download: toMB(recvRate), - }; - }, [isLive, latestMetrics, selectedInterface]); - - const chartData = useLiveBuffer(initialData, !!isLive, livePoint, LIVE_WINDOW_MS, `${agentId}|${selectedInterface}`); - - // 网卡选择器 - const interfaceSelector = availableInterfaces.length > 0 && ( - - ); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - return ( - - {chartData.length > 0 ? ( - - - - - - - - - - - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - height={45} - /> - `${value} MB`} - /> - }/> - - - - - - ) : ( - - )} - - ); -}; - -/* ========================================== NetworkConnectionChart ========================================== */ - -interface ConnPoint { - timestamp: number; - established: number; - time_wait: number; - close_wait: number; - listen: number; -} - -const NetworkConnectionChart = ({agentId, timeRange, start, end, isLive, latestMetrics}: ChartPropsBase) => { - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'network_connection', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - }); - - // 历史数据 - const initialData = useMemo(() => { - if (!metricsResponse?.series || metricsResponse.series.length === 0) return []; - - const timeMap = new Map(); - - metricsResponse.series.forEach(series => { - const stateName = series.name; - series.data.forEach(point => { - if (!timeMap.has(point.timestamp)) { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - established: 0, - time_wait: 0, - close_wait: 0, - listen: 0, - }); - } - const existing = timeMap.get(point.timestamp)!; - if (stateName === 'established' || stateName === 'time_wait' || stateName === 'close_wait' || stateName === 'listen') { - existing[stateName] = Number(point.value.toFixed(0)); - } - }); - }); - - return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp); - }, [metricsResponse]); - - // 实时点 - const livePoint = useMemo(() => { - if (!isLive || !latestMetrics?.networkConnection || !latestMetrics.timestamp) return null; - const c = latestMetrics.networkConnection; - return { - timestamp: latestMetrics.timestamp, - established: c.established ?? 0, - time_wait: c.timeWait ?? 0, - close_wait: c.closeWait ?? 0, - listen: c.listen ?? 0, - }; - }, [isLive, latestMetrics]); - - const chartData = useLiveBuffer(initialData, !!isLive, livePoint, LIVE_WINDOW_MS, agentId); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - return ( - - {chartData.length > 0 ? ( - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - height={45} - /> - - }/> - - - - - - - - ) : ( - - )} - - ); -}; - -/* ========================================== GpuChart ========================================== */ - -const GpuChartImpl = ({agentId, timeRange, start, end, isLive}: ChartPropsBase) => { - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - // 数据查询 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'gpu', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - refetchIntervalMs: isLive ? 5000 : undefined, - }); - - // 数据转换 - const chartData = useMemo(() => { - if (!metricsResponse?.series || metricsResponse.series.length === 0) return []; - - const timeMap = new Map(); - - const utilizationSeries = metricsResponse.series.find(s => s.name === 'utilization'); - const temperatureSeries = metricsResponse.series.find(s => s.name === 'temperature'); - - utilizationSeries?.data.forEach(point => { - const existing = timeMap.get(point.timestamp); - if (existing) { - existing.utilization = Number(point.value.toFixed(2)); - } else { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - utilization: Number(point.value.toFixed(2)), - }); - } - }); - - temperatureSeries?.data.forEach(point => { - const existing = timeMap.get(point.timestamp); - if (existing) { - existing.temperature = Number(point.value.toFixed(2)); - } else { - timeMap.set(point.timestamp, { - timestamp: point.timestamp, - temperature: Number(point.value.toFixed(2)), - }); - } - }); - - return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp); - }, [metricsResponse]); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - // 如果没有 GPU 数据,不渲染组件 - if (chartData.length === 0) { - return null; - } - - return ( - - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - className="stroke-gray-400 dark:stroke-cyan-600" - style={{fontSize: '12px'}} - /> - `${value}%`} - /> - `${value}°C`} - /> - }/> - - - - - - - ); -}; - -const GpuChart = memo(GpuChartImpl); - -/* ========================================== TemperatureChart ========================================== */ - -const TemperatureChartImpl = ({agentId, timeRange, start, end, isLive}: ChartPropsBase) => { - const [selectedTempType, setSelectedTempType] = useState('all'); - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const effectiveRange = isLive ? LIVE_INITIAL_RANGE : timeRange; - - // 数据查询:温度采集 5s 一次,实时模式 5s 重查 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'temperature', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - refetchIntervalMs: isLive ? 5000 : undefined, - }); - - // 数据转换 - const chartData = useMemo(() => { - if (!metricsResponse?.series || metricsResponse.series.length === 0) return []; - - const timeMap = new Map(); - - metricsResponse.series.forEach(series => { - const sensorName = series.name; - series.data.forEach(point => { - if (!timeMap.has(point.timestamp)) { - timeMap.set(point.timestamp, {timestamp: point.timestamp}); - } - - const existing = timeMap.get(point.timestamp)!; - existing[sensorName] = Number(point.value.toFixed(2)); - }); - }); - - return Array.from(timeMap.values()); - }, [metricsResponse]); - - // 提取所有唯一的温度类型 - const temperatureTypes = useMemo(() => { - return metricsResponse?.series?.map(s => s.name).sort() || []; - }, [metricsResponse]); - - // 根据选中的类型过滤温度数据 - const filteredTemperatureTypes = useMemo(() => { - if (selectedTempType === 'all') { - return temperatureTypes; - } - return temperatureTypes.filter(type => type === selectedTempType); - }, [temperatureTypes, selectedTempType]); - - // 当温度类型列表变化时,如果当前选中的类型不在列表中,重置为 'all' - useEffect(() => { - if (selectedTempType !== 'all' && temperatureTypes.length > 0) { - if (!temperatureTypes.includes(selectedTempType)) { - setSelectedTempType('all'); - } - } - }, [temperatureTypes, selectedTempType]); - - // 温度类型选择器 - const tempTypeSelector = temperatureTypes.length > 1 && ( - - ); - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - // 如果没有温度数据,不渲染组件 - if (chartData.length === 0 || temperatureTypes.length === 0) { - return null; - } - - return ( - - - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - height={45} - /> - `${value}°C`} - /> - }/> - - {/* 为选中的温度类型渲染线条 */} - {filteredTemperatureTypes.map((type, index) => { - const color = TEMPERATURE_COLORS[type] || `hsl(${(index * 60) % 360}, 70%, 50%)`; - return ( - - ); - })} - - - - ); -}; - -const TemperatureChart = memo(TemperatureChartImpl); - -/* ========================================== MonitorChart ========================================== */ - -/** - * 降采样算法 - 使用LTTB (Largest Triangle Three Buckets) - * 确保输出精确的maxPoints个点,保留关键特征 - */ -const downsampleData = (data: any[], maxPoints: number): any[] => { - // 边界检查 - if (!data || data.length === 0) return []; - if (maxPoints < 2) maxPoints = 2; - if (data.length <= maxPoints) return [...data]; - - const result: any[] = [data[0]]; // 保留第一个点 - - // 桶大小 - const bucketSize = (data.length - 2) / (maxPoints - 2); - - for (let i = 0; i < maxPoints - 2; i++) { - // 计算当前桶的范围 - const start = Math.floor((i + 0) * bucketSize) + 1; - const end = Math.floor((i + 1) * bucketSize) + 1; - - // 计算前一个点和后一个点 - const previousPoint = result[result.length - 1]; - const nextPoint = data[Math.min(end, data.length - 1)]; - - // 在桶中选择与前后点形成的三角形面积最大的点 - let maxArea = -1; - let selectedPoint = data[start]; - - for (let j = start; j < end && j < data.length - 1; j++) { - // 计算三角形面积 - const area = Math.abs( - (previousPoint.timestamp - nextPoint.timestamp) * (data[j].value - previousPoint.value) - - (previousPoint.timestamp - data[j].timestamp) * (nextPoint.value - previousPoint.value) - ); - - if (area > maxArea) { - maxArea = area; - selectedPoint = data[j]; - } - } - - result.push(selectedPoint); - } - - result.push(data[data.length - 1]); // 保留最后一个点 - - return result; -}; - -/** - * 根据时间范围确定最大数据点数 - */ -const getMaxDataPoints = (timeRange: string): number => { - switch (timeRange) { - case '15m': - case '1h': - return 200; // 短时间:详细数据 - case '12h': - return 300; - case '24h': - return 400; - case '7d': - return 500; - case '30d': - return 600; - default: - return 400; - } -}; - -/** - * 生成不重复的颜色 - * 使用 HSL 色轮均匀分布,支持无限数量的监控项 - */ -const generateColors = (count: number): string[] => { - const colors: string[] = []; - const hueStep = 360 / count; // 色相间隔 - - for (let i = 0; i < count; i++) { - const hue = (i * hueStep) % 360; - const saturation = 65 + (i % 3) * 10; // 65%, 75%, 85% 循环 - const lightness = 45 + (i % 2) * 10; // 45%, 55% 循环 - colors.push(`hsl(${hue}, ${saturation}%, ${lightness}%)`); - } - - return colors; -}; - -/** - * 自定义图例组件 - */ -const CustomLegend = ({ onClick, selectedMonitors, allMonitorKeys, colors, collapsed }: any) => { - if (!allMonitorKeys || allMonitorKeys.length === 0) return null; - - if (collapsed) return null; - - return ( -
- {allMonitorKeys.map((monitorKey: string, index: number) => { - const isSelected = selectedMonitors.has(monitorKey); - const color = colors[index]; - - return ( -
onClick({ value: monitorKey })} - className="flex items-center gap-2 cursor-pointer transition-opacity" - style={{ - opacity: isSelected ? 1 : 0.4, - }} - > - - - - - {monitorKey} - -
- ); - })} -
- ); -}; - -const MonitorChartImpl = ({agentId, timeRange, start, end, isLive}: ChartPropsBase) => { - const isMobile = useIsMobile(); - const rangeMs = start !== undefined && end !== undefined ? end - start : undefined; - const [selectedMonitors, setSelectedMonitors] = useState>(new Set()); - const [legendCollapsed, setLegendCollapsed] = useState(true); // 移动端默认收起 - // 监控任务由探针自定义周期上报,实时模式下保留 15m 视图,10s 重查 - const effectiveRange = isLive ? '15m' : timeRange; - - // 数据查询 - const {data: metricsResponse, isLoading} = useMetricsQuery({ - agentId, - type: 'monitor', - range: start !== undefined && end !== undefined ? undefined : effectiveRange, - start, - end, - refetchIntervalMs: isLive ? 10000 : undefined, - }); - - // 获取所有监控任务的列表(使用名称) - const allMonitorKeys = useMemo(() => { - const series = metricsResponse?.series || []; - return series.map(s => s.labels?.monitor_name || s.labels?.monitor_id || s.name); - }, [metricsResponse]); - - // 初始化选中所有监控任务 - useEffect(() => { - if (allMonitorKeys.length > 0 && selectedMonitors.size === 0) { - setSelectedMonitors(new Set(allMonitorKeys)); - } - }, [allMonitorKeys, selectedMonitors.size]); - - // 过滤后的监控任务列表 - const monitorKeys = useMemo(() => { - return allMonitorKeys.filter(key => selectedMonitors.has(key)); - }, [allMonitorKeys, selectedMonitors]); - - // 数据转换 - 支持多个监控任务(统一时间轴 + 线性插值) - const chartData = useMemo(() => { - const series = metricsResponse?.series || []; - if (series.length === 0) return []; - - // 收集所有监控任务的数据 - const seriesDataArray: Array<{ key: string; data: Array<{ timestamp: number; value: number }> }> = []; - - series.forEach((s) => { - const monitorKey = s.labels?.monitor_name || s.labels?.monitor_id || s.name; - if (!selectedMonitors.has(monitorKey)) return; - if (!s.data || s.data.length === 0) return; - - seriesDataArray.push({ - key: monitorKey, - data: [...s.data].sort((a, b) => a.timestamp - b.timestamp) - }); - }); - - if (seriesDataArray.length === 0) return []; - - // 取所有监控任务时间范围的交集,确保每个时间点所有任务都有数据 - let minTime = -Infinity, maxTime = Infinity; - seriesDataArray.forEach(s => { - if (s.data.length > 0) { - minTime = Math.max(minTime, s.data[0].timestamp); - maxTime = Math.min(maxTime, s.data[s.data.length - 1].timestamp); - } - }); - - // 如果没有交集,返回空数组 - if (minTime >= maxTime) return []; - - // 均匀生成目标时间点 - const maxPoints = getMaxDataPoints(timeRange); - const timeStep = (maxTime - minTime) / (maxPoints - 1); - const targetTimestamps: number[] = []; - for (let i = 0; i < maxPoints; i++) { - targetTimestamps.push(minTime + i * timeStep); - } - - // 线性插值函数 - const interpolate = (data: Array<{ timestamp: number; value: number }>, targetTime: number): number | null => { - if (data.length === 0) return null; - if (data.length === 1) { - // 单点数据,只有精确匹配才返回 - return data[0].timestamp === targetTime ? data[0].value : null; - } - - // 如果目标时间在数据范围外,返回 null(断开折线) - if (targetTime < data[0].timestamp || targetTime > data[data.length - 1].timestamp) { - return null; - } - - // 二分查找找到 targetTime 前后两个点 - let left = 0, right = data.length - 1; - while (right - left > 1) { - const mid = Math.floor((left + right) / 2); - if (data[mid].timestamp <= targetTime) { - left = mid; - } else { - right = mid; - } - } - - // 线性插值 - const leftPoint = data[left]; - const rightPoint = data[right]; - const ratio = (targetTime - leftPoint.timestamp) / (rightPoint.timestamp - leftPoint.timestamp); - return leftPoint.value + ratio * (rightPoint.value - leftPoint.value); - }; - - // 对每个时间点,从每个监控任务中插值获取值 - return targetTimestamps.map(timestamp => { - const dataPoint: any = { timestamp }; - seriesDataArray.forEach(s => { - const value = interpolate(s.data, timestamp); - if (value !== null) { - dataPoint[s.key] = Number(value.toFixed(2)); - } - }); - return dataPoint; - }); - }, [metricsResponse, selectedMonitors, timeRange, start, end]); - - // 动态生成颜色(根据监控项数量) - const colors = useMemo(() => { - return generateColors(allMonitorKeys.length); - }, [allMonitorKeys.length]); - - const toggleMonitor = (monitorKey: string) => { - setSelectedMonitors((current) => { - if (current.size === allMonitorKeys.length) return new Set([monitorKey]); - const next = new Set(current); - if (next.has(monitorKey)) next.delete(monitorKey); - else next.add(monitorKey); - return next; - }); - }; - - const handleAreaClick = (data: unknown) => { - const key = (data as {dataKey?: string})?.dataKey; - if (key) toggleMonitor(key); - }; - - const handleLegendClick = (data: {value?: string}) => { - if (data?.value) toggleMonitor(data.value); - }; - - // 恢复全选 - const handleSelectAll = () => { - setSelectedMonitors(new Set(allMonitorKeys)); - }; - - // 是否有监控项未选中(用于显示恢复按钮) - const hasUnselected = selectedMonitors.size < allMonitorKeys.length; - - // 切换图例显示/隐藏(仅移动端) - const toggleLegend = () => setLegendCollapsed((collapsed) => !collapsed); - - // 如果没有数据且不是加载中,不渲染组件 - if (!isLoading && chartData.length === 0) { - return null; - } - - // 渲染 - if (isLoading) { - return ( - - - - ); - } - - return ( - - {chartData.length > 0 ? ( - <> - {/* 使用提示和恢复按钮 */} - {allMonitorKeys.length > 1 && ( -
-
- 💡 点击图表线条或图例切换显示 -
- {hasUnselected && ( - - )} -
- )} - - - - - {monitorKeys.map((key, index) => { - const originalIndex = allMonitorKeys.indexOf(key); - return ( - - - - - ); - })} - - - formatChartTime(Number(value), timeRange, rangeMs)} - stroke="currentColor" - angle={-15} - textAnchor="end" - className="text-xs text-gray-600 dark:text-cyan-500 font-mono" - height={45} - /> - `${value}ms`} - /> - } - wrapperStyle={{zIndex: 9999,}} - /> - {monitorKeys.map((key, index) => { - const originalIndex = allMonitorKeys.indexOf(key); - return ( - - ); - })} - - - - {/* 桌面端:直接显示图例 */} - {!isMobile && allMonitorKeys.length > 0 && ( - - )} - - {/* 移动端:可折叠图例 */} - {isMobile && allMonitorKeys.length > 0 && ( -
- - -
- )} - - ) : ( - - )} -
- ); -}; - -const MonitorChart = memo(MonitorChartImpl); - -/* ========================================== ServerDetail ========================================== */ - -/** - * 服务器详情页面 - * 显示服务器的详细信息、实时指标和历史趋势图表 - */ -const ServerDetail = () => { - const {id} = useParams<{ id: string }>(); - const navigate = useNavigate(); - const [timeRange, setTimeRange] = useState(LIVE_RANGE); - const [customRange, setCustomRange] = useState<{ start: number; end: number } | null>(null); - - const handleCustomRangeApply = (range: { start: number; end: number }) => { - setCustomRange(range); - }; - - const isLive = timeRange === LIVE_RANGE; - const customStart = timeRange === 'custom' ? customRange?.start : undefined; - const customEnd = timeRange === 'custom' ? customRange?.end : undefined; - - // 查询基础数据(用于页面头部和系统信息) - // 实时模式 1s 拉取最新指标,其余 5s - const {data: agentResponse, isLoading} = useAgentQuery(id); - const {data: latestMetricsResponse} = useLatestMetricsQuery(id, isLive ? 1000 : 5000); - - const agent = agentResponse; - const latestMetrics = latestMetricsResponse || null; - - const deviceIpInterfaces = (latestMetrics?.networkInterfaces || []) - .map((netInterface) => ({ - name: netInterface.interface, - addrs: Array.from(new Set((netInterface.addrs || []).map((addr) => addr.trim()).filter(Boolean))), - })) - .filter((netInterface) => netInterface.addrs.length > 0); - - if (isLoading) { - return ; - } - - if (!agent) { - return ; - } - - return ( -
-
- {/* 头部区域 */} - navigate('/')} - /> - - {/* 主内容区 */} -
- {/* 网络地址信息 */} - {(agent.ipv4 || agent.ipv6 || deviceIpInterfaces?.length > 0) && ( - - )} - - {/* 系统信息 */} - - - {/* 历史趋势图表 */} - - -
- } - > -
- {/* 核心指标:大屏 2 列,小屏 1 列 */} -
- - -
- - {/* 网络相关:大屏 2 列,中屏 1 列 */} -
- - -
- - {/* 进阶指标:单列全宽 */} -
- -
- - {/* 硬件指标:条件渲染,单列全宽 */} -
- - -
- - {/* 监控指标:单列全宽 */} -
- -
-
- - - {/* 网络连接统计 */} - - - {/* GPU 监控 */} - - - {/* 温度监控 */} - - -
-
- ); -}; - -export default ServerDetail; diff --git a/portal/src/pages/ServerList.tsx b/portal/src/pages/ServerList.tsx deleted file mode 100644 index 9223973..0000000 --- a/portal/src/pages/ServerList.tsx +++ /dev/null @@ -1,848 +0,0 @@ -import {type FC, type ReactNode, useMemo, useState} from 'react'; -import {Link, useNavigate} from 'react-router-dom'; -import {useQuery} from '@tanstack/react-query'; -import * as Tooltip from '@radix-ui/react-tooltip'; -import { - Activity, - AlertTriangle, - ArrowDown, - ArrowUp, - Calendar, - Clock, - Cpu, - Filter, - Globe, - HardDrive, - LinkIcon, - MemoryStick, - Network, - Thermometer, - UnlinkIcon -} from 'lucide-react'; -import {pika} from '../api'; -import type {Agent, LatestMetrics, TagsResponse} from '../types'; -import {cn, formatBytes, formatSpeed, formatTime, formatUptime, isExpired} from '../lib/utils'; -import {Card, LoadingSpinner, StatBlock} from '../components'; - -interface AgentWithMetrics extends Agent { - metrics?: LatestMetrics; -} - -/* ========================================== CompactResourceBar ========================================== */ - -// 紧凑型资源条组件 -const CompactResourceBar = ({value, label, subtext, icon: Icon, color = "bg-cyan-500"}: { - value: number; - label: string; - subtext?: string; - icon: any; - color?: string; -}) => { - const isCritical = value > 90; - const isWarning = value > 75; - - // 颜色定义 (Hex codes for precise control) - let barColor = ""; - let iconClass = ""; - let textClass = "dark:text-cyan-50"; // 默认高亮白/青 - - if (isCritical) { - barColor = "bg-rose-500"; - iconClass = "text-rose-600 dark:text-rose-500"; - textClass = "text-rose-400"; - } else if (isWarning) { - barColor = "bg-amber-500"; - iconClass = "text-amber-600 dark:text-amber-400"; - textClass = "text-amber-400"; - } else if (color.includes("purple")) { - barColor = "bg-purple-500"; - iconClass = "text-purple-600 dark:text-purple-400"; - } else if (color.includes("blue")) { - barColor = "bg-blue-500"; - iconClass = "text-blue-600 dark:text-blue-400"; - } else { - barColor = "bg-cyan-500"; - iconClass = "text-cyan-600 dark:text-cyan-400"; - } - - return ( -
- - - -
- {/* Icon & Label */} -
- - {label} -
- - {/* Track Container */} -
- - {/* Scale Marks Background (The "Ruler" effect) */} -
- -
-
-
-
-
- {value.toFixed(1)}% -
-
-
- - - {subtext} - - - -
-
-
- ); -}; - -/* ========================================== 共享辅助函数 ========================================== */ - -const calculateNetworkSpeed = (metrics?: LatestMetrics) => { - if (!metrics?.network) { - return {upload: 0, download: 0}; - } - return { - upload: metrics.network.totalBytesSentRate, - download: metrics.network.totalBytesRecvRate - }; -}; - -const calculateDiskUsage = (metrics?: LatestMetrics) => { - if (!metrics?.disk) { - return 0; - } - return metrics.disk.usagePercent; -}; - -const getTemperatures = (metrics?: LatestMetrics) => { - if (!metrics?.temperature || metrics.temperature.length === 0) { - return []; - } - // 返回所有温度数据 - return metrics.temperature.sort((a, b) => a.type.localeCompare(b.type)); -}; - -const getTrafficProgressColor = (percent: number) => { - if (percent >= 100) return 'bg-red-500'; - if (percent >= 90) return 'bg-orange-500'; - if (percent >= 80) return 'bg-yellow-500'; - return 'bg-emerald-500'; -}; - -/* ========================================== ServerCard ========================================== */ - -interface ServerCardProps { - server: AgentWithMetrics; -} - -const ServerCard: FC = ({server}) => { - const isOnline = server.status === 1; - const cpuUsage = server.metrics?.cpu?.usagePercent ?? 0; - const memoryUsage = server.metrics?.memory?.usagePercent ?? 0; - const memoryTotal = server.metrics?.memory?.total ?? 0; - const memoryUsed = server.metrics?.memory?.used ?? 0; - const diskUsage = calculateDiskUsage(server.metrics); - const diskTotal = server.metrics?.disk?.total ?? 0; - const diskUsed = server.metrics?.disk?.used ?? 0; - const {upload, download} = calculateNetworkSpeed(server.metrics); - const temperatures = getTemperatures(server.metrics); - const netConn = server.metrics?.networkConnection; - const traffic = server.trafficStats; - const trafficUsagePercent = traffic?.enabled && traffic.limit > 0 - ? Math.min(100, (traffic.used / traffic.limit) * 100) - : 0; - - return ( - - -
- {/* 顶部:名称和状态 */} -
-
-
- {server.name || server.hostname} -
-
- {server.os} - - {server.arch} -
-
- {server.tags && server.tags.length > 0 && ( -
- {server.tags.slice(0, 2).map(tag => ( - - #{tag} - - ))} - {server.tags.length > 2 && ( - - +{server.tags.length - 2} - - )} -
- )} -
- - {isOnline && server.metrics?.host && ( -
-
- - {formatUptime(server.metrics.host.uptime)} -
- -
- - {server.metrics.host.procs} 进程 -
-
- )} - - {/* 资源使用情况 */} - {isOnline ? ( -
- - - - {temperatures.length > 0 && ( -
- - {temperatures.map((temp, index) => ( - - {temp.temperature?.toFixed(1)}°C - {temp.type} - {index < temperatures.length - 1 && - |} - - ))} -
- )} -
- ) : ( -
- - 离线 -
- )} - - {/* 网络和流量 */} -
-
-
- - - {formatSpeed(download)} - - - - {formatSpeed(upload)} - -
- {server.expireTime > 0 && ( -
- - {new Date(server.expireTime).toLocaleDateString('zh-CN')} -
- )} -
- {isOnline && netConn && ( -
- - - {netConn.established || 0} - ESTABLISHED - - - - {netConn.listen || 0} - LISTEN - - - - {netConn.closeWait || 0} - CLOSE_WAIT - -
- )} - {traffic?.enabled && ( -
-
- - {traffic.type === 'recv' ? '进站' : traffic.type === 'send' ? '出站' : '全部'}流量 -
- {traffic.limit > 0 ? ( - <> -
- - {formatBytes(traffic.used, 1)} / {formatBytes(traffic.limit, 1)} - - - {trafficUsagePercent.toFixed(1)}% - -
-
-
-
-
- 重置日期: 每月 {traffic.resetDay} 号 -
- - ) : ( -
- 已使用: {formatBytes(traffic.used, 1)} -
仅统计模式
-
- )} -
- )} -
-
- - - ); -}; - -/* ========================================== ServerList 本地组件 ========================================== */ - -const NetworkStatCard = ({uploadRate, downloadRate, uploadTotal, downloadTotal}: { - uploadRate: number; - downloadRate: number; - uploadTotal: number; - downloadTotal: number; -}) => ( -
- -
-
-
网络统计
-
-
- - {formatSpeed(uploadRate)} - ({formatBytes(uploadTotal)}) -
-
- - {formatSpeed(downloadRate)} - ({formatBytes(downloadTotal)}) -
-
-
- -
-
-); - -interface ServerListEmptyProps { - title: string; - description: string; - extra?: ReactNode; -} - -const ServerListEmpty = ({title, description, extra}: ServerListEmptyProps) => ( -
-
- -
-

{title}

-

{description}

- {extra ?
{extra}
: null} -
-); - -/* ========================================== ServerList ========================================== */ - -const ServerList = () => { - const navigate = useNavigate(); - const [selectedTag, setSelectedTag] = useState(''); - - const {data: agents = [], isLoading} = useQuery({ - queryKey: ['agents', 'online'], - queryFn: () => pika.listAgents(), - refetchInterval: 3000, - }); - - // 获取标签列表 - const {data: tagsData} = useQuery({ - queryKey: ['tags', 'public'], - queryFn: async () => { - const response = await pika.getTags(); - return response.tags || []; - }, - refetchInterval: 30000, - }); - - // 计算所有标签(包括ALL和ONLINE/OFFLINE) - const allTags = useMemo(() => { - const tags = ['ALL', 'ONLINE', 'OFFLINE']; - if (tagsData && tagsData.length > 0) { - tagsData.forEach((tag: string) => { - if (!tags.includes(tag.toUpperCase())) { - tags.push(tag.toUpperCase()); - } - }); - } - return tags; - }, [tagsData]); - - // 过滤逻辑 - const displayAgents = useMemo(() => { - if (selectedTag === 'ONLINE') { - return agents.filter(a => a.status === 1); - } else if (selectedTag === 'OFFLINE') { - return agents.filter(a => a.status !== 1); - } else if (selectedTag && selectedTag !== 'ALL') { - return agents.filter(a => a.tags?.map(t => t.toUpperCase()).includes(selectedTag)); - } - return agents; - }, [agents, selectedTag]); - - // 计算统计数据(基于过滤后的 displayAgents) - const stats = useMemo(() => { - const total = displayAgents.length; - const online = displayAgents.filter(a => a.status === 1).length; - const offline = total - online; - - // 计算网络统计 - let totalUploadRate = 0; - let totalDownloadRate = 0; - let totalUploadTotal = 0; - let totalDownloadTotal = 0; - - displayAgents.forEach(agent => { - if (agent.status === 1 && agent.metrics?.network) { - totalUploadRate += agent.metrics.network.totalBytesSentRate || 0; - totalDownloadRate += agent.metrics.network.totalBytesRecvRate || 0; - totalUploadTotal += agent.metrics.network.totalBytesSentTotal || 0; - totalDownloadTotal += agent.metrics.network.totalBytesRecvTotal || 0; - } - }); - - return { - total, - online, - offline, - uploadRate: totalUploadRate, - downloadRate: totalDownloadRate, - uploadTotal: totalUploadTotal, - downloadTotal: totalDownloadTotal - }; - }, [displayAgents]); - - const handleNavigate = (agentId: string) => { - navigate(`/servers/${agentId.substring(0, 8)}`); - }; - - if (isLoading) { - return ; - } - - // debug - // displayAgents = Array.from({length:10}, ()=>displayAgents).flat(); - - return ( -
- {/* 统计卡片 */} -
- - - 0} - /> - -
- - {/* 标签过滤器 */} - {allTags.length > 1 && ( -
-
- - FILTERS: -
- {allTags.map(tag => { - const tagKey = tag === 'ALL' ? '' : tag; - let count = 0; - if (tag === 'ALL') count = agents.length; - else if (tag === 'ONLINE') count = agents?.filter(a => a.status === 1).length; - else if (tag === 'OFFLINE') count = agents?.filter(a => a.status !== 1).length; - else count = agents?.filter(a => a.tags?.map(t => t.toUpperCase()).includes(tag)).length; - - if (count === 0 && tag !== 'ALL') return null; - - return ( - - ); - })} -
- )} - - {/* 服务器列表 */} - {displayAgents.length === 0 ? ( - - ) : ( - <> - {/* 桌面端表格布局 */} -
- - - - - - - - - - - - - {displayAgents.map(server => { - const isOnline = server.status === 1; - const cpuUsage = server.metrics?.cpu?.usagePercent ?? 0; - const memoryUsage = server.metrics?.memory?.usagePercent ?? 0; - const memoryTotal = server.metrics?.memory?.total ?? 0; - const memoryUsed = server.metrics?.memory?.used ?? 0; - const diskUsage = calculateDiskUsage(server.metrics); - const diskTotal = server.metrics?.disk?.total ?? 0; - const diskUsed = server.metrics?.disk?.used ?? 0; - const {upload, download} = calculateNetworkSpeed(server.metrics); - const temperatures = getTemperatures(server.metrics); - const netConn = server.metrics?.networkConnection; - const traffic = server.trafficStats; - const trafficUsagePercent = traffic?.enabled && traffic.limit > 0 - ? Math.min(100, (traffic.used / traffic.limit) * 100) - : 0; - - return ( - handleNavigate(server.id)} - onKeyDown={(event) => { - if (event.key === 'Enter' || event.key === ' ') { - event.preventDefault(); - handleNavigate(server.id); - } - }} - className="group hover:bg-gray-500/5 dark:hover:bg-cyan-500/5 transition-colors cursor-pointer" - > - {/* Identity */} - - - {/* Resources */} - - - {/* Network */} - - - {/* Traffic */} - - - {/* Connections */} - - - {/* Meta */} - - - ); - })} - -
IdentityTelemetryI/O RateTrafficNetworkMeta / Tags
-
-
-
- {server.name} -
-
- {server.os} - - {server.arch} -
- {isOnline && server.metrics?.host && ( -
-
- - {formatUptime(server.metrics.host.uptime)} -
-
- - {server.metrics.host.procs} 进程 -
-
- )} -
-
-
- {isOnline ? ( -
- - - - {temperatures.length > 0 && ( -
- - {temperatures.map((temp, index) => ( - - {temp.temperature?.toFixed(1)}°C - {temp.type} - {index < temperatures.length - 1 && - |} - - ))} -
- )} -
- ) : ( -
- - CONNECTION_LOST // RECONNECTING... -
- )} -
-
- - - {formatSpeed(download)} - - - - {formatSpeed(upload)} - -
-
- {traffic?.enabled ? ( -
-
- {traffic.type === 'recv' ? '进站' : traffic.type === 'send' ? '出站' : '全部'}流量 -
- {traffic.limit > 0 ? ( - <> -
- - {formatBytes(traffic.used, 1)} / {formatBytes(traffic.limit, 1)} - - - {trafficUsagePercent.toFixed(1)}% - -
-
-
-
-
- 重置日期: 每月 {traffic.resetDay} 号 -
- - ) : ( -
- 已使用: {formatBytes(traffic.used, 1)} -
仅统计模式
-
- )} -
- ) : ( -
-
- )} -
- {isOnline && netConn ? ( -
-
- - {netConn.established || 0} - ESTABLISHED -
-
- - {netConn.listen || 0} - LISTEN -
-
- - {netConn.closeWait || 0} - CLOSE_WAIT -
-
- ) : ( -
-
- )} -
-
-
- {server.tags && server.tags.length > 0 && server.tags.map(tag => ( - - #{tag} - - ))} -
-
- - {server.expireTime > 0 && -
-
Expired: {new Date(server.expireTime).toLocaleDateString('zh-CN')}
-
- } -
-
-
-
- - {/* 移动端卡片布局 */} -
- {displayAgents.map(server => ( - - ))} -
- - )} -
- ); -}; - -export default ServerList; diff --git a/portal/src/types.ts b/portal/src/types.ts deleted file mode 100644 index 74d37d8..0000000 --- a/portal/src/types.ts +++ /dev/null @@ -1,255 +0,0 @@ -/* 主题前端使用的全部类型定义。 */ - -export type ColorMode = 'light' | 'dark' | 'system'; -export type MetricsAggregation = 'avg' | 'max' | 'raw'; - -export interface PikaRuntimeConfig { - apiVersion: 'v1'; - system: { - nameZh: string; - nameEn: string; - logo: string; - icpCode: string; - version: string; - defaultView: string; - defaultColorMode: ColorMode; - }; - theme: {id: string; version: string}; - features: Record; - legacySystemConfig?: LegacySystemConfig; -} - -interface LegacySystemConfig { - SystemNameZh: string; - SystemNameEn: string; - ICPCode: string; - DefaultView: string; - Version: string; -} - -export interface ListAgentParams {tag?: string; page?: number; pageSize?: number; [key: string]: string | number | boolean | undefined} -export interface MetricsParams { - type?: 'cpu' | 'memory' | 'disk' | 'network' | 'network_connection' | 'disk_io' | 'gpu' | 'temperature' | 'monitor'; - range?: string; - start?: number; - end?: number; - interface?: string; - aggregation?: MetricsAggregation; - [key: string]: string | number | boolean | undefined; -} -export interface HistoryParams { - range?: string; - start?: number; - end?: number; - aggregation?: MetricsAggregation; - [key: string]: string | number | boolean | undefined; -} -export interface TimeRangeOption {label: string; value: string} - -export interface Agent { - id: string; - name: string; - hostname: string; - ip: string; - ipv4?: string; - ipv6?: string; - os: string; - arch: string; - version: string; - tags?: string[]; - expireTime?: number; - status: number; - visibility?: string; - weight?: number; - remark?: string; - lastSeenAt: string | number; - trafficStats?: { - enabled: boolean; - type: string; - limit: number; - used: number; - resetDay: number; - periodStart: number; - periodEnd: number; - daysUntilReset: number; - baselineRecv: number; - baselineSend: number; - alertSent80: boolean; - alertSent90: boolean; - alertSent100: boolean; - }; -} - -export interface CPUMetric { - timestamp: number; - logicalCores: number; - physicalCores: number; - modelName: string; - usagePercent: number; -} - -export interface MemoryMetric { - timestamp: number; - total: number; - used: number; - free: number; - available: number; - usagePercent: number; - swapTotal: number; - swapUsed: number; - swapFree: number; -} - -export interface DiskSummary { - usagePercent: number; - totalDisks: number; - total: number; - used: number; - free: number; -} - -export interface DiskIOSummary { - totalReadBytesRate: number; - totalWriteBytesRate: number; - totalDevices: number; -} - -export interface NetworkSummary { - totalBytesSentRate: number; - totalBytesRecvRate: number; - totalBytesSentTotal: number; - totalBytesRecvTotal: number; - totalInterfaces: number; -} - -export interface NetworkInterfaceMetric { - interface: string; - macAddress?: string; - addrs?: string[]; - bytesSentRate: number; - bytesRecvRate: number; - bytesSentTotal: number; - bytesRecvTotal: number; -} - -export interface NetworkConnectionMetric { - established: number; - synSent: number; - synRecv: number; - finWait1: number; - finWait2: number; - timeWait: number; - close: number; - closeWait: number; - lastAck: number; - listen: number; - closing: number; - total: number; -} - -export interface HostInfo { - uptime: number; - bootTime: number; - procs: number; - load1: number; - load5: number; - load15: number; - platform: string; - platformVersion: string; - kernelVersion: string; - kernelArch: string; -} - -export interface GPUMetric { - index: number; - name: string; - utilization: number; - memoryTotal: number; - memoryUsed: number; - memoryFree: number; - temperature: number; - powerUsage: number; - fanSpeed: number; -} - -export interface TemperatureMetric { - sensorKey: string; - type: string; - temperature: number; -} - -export interface MonitorData { - agentId: string; - agentName: string; - monitorId: string; - monitorName?: string; - type: string; - target?: string; - status: string; - statusCode?: number; - responseTime: number; - error?: string; - checkedAt: number; - message?: string; - contentMatch?: boolean; - certExpiryTime?: number; - certDaysLeft?: number; -} - -export interface LatestMetrics { - timestamp?: number; - cpu?: CPUMetric; - memory?: MemoryMetric; - disk?: DiskSummary; - diskIO?: DiskIOSummary; - network?: NetworkSummary; - networkInterfaces?: NetworkInterfaceMetric[]; - networkConnection?: NetworkConnectionMetric; - host?: HostInfo; - gpu?: GPUMetric[]; - temperature?: TemperatureMetric[]; - monitors?: MonitorData[]; -} - -export interface PublicMonitor { - id: string; - name: string; - type: 'http' | 'https' | 'tcp' | 'icmp' | 'ping'; - target: string; - showTargetPublic: boolean; - description?: string; - enabled: boolean; - interval: number; - agentIds: string[]; - agentCount: number; - status: string; - responseTime: number; - responseTimeMin: number; - responseTimeMax: number; - certExpiryTime: number; - certDaysLeft: number; - agentStats: {up: number; down: number; unknown: number}; - lastCheckTime: number; -} - -export interface AgentMonitorStat { - agentId: string; - agentName: string; - monitorId: string; - type: string; - target: string; - status: string; - statusCode: number; - responseTime: number; - checkedAt: number; - message: string; - certExpiryTime: number; - certDaysLeft: number; -} - -export interface MetricPoint {timestamp: number; value: number} -export interface MetricSeries {name: string; labels?: Record; data: MetricPoint[]} -export interface MetricsResponse {agentId: string; type: string; range: string; series: MetricSeries[]} -export interface TagsResponse {tags: string[]} -export interface NetworkInterfacesResponse {interfaces: string[]} -export interface CurrentUser {userId: string; username: string} diff --git a/portal/tsconfig.app.json b/portal/tsconfig.app.json deleted file mode 100644 index bf71a42..0000000 --- a/portal/tsconfig.app.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "module": "ESNext", - "types": ["vite/client"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - "strict": false, - - "paths": { - "@/*": [ - "./src/*" - ] - }, - "allowSyntheticDefaultImports": true - }, - "include": ["src"] -} diff --git a/portal/tsconfig.json b/portal/tsconfig.json deleted file mode 100644 index 4fa15f4..0000000 --- a/portal/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.node.json" - } - ], - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - } -} diff --git a/portal/tsconfig.node.json b/portal/tsconfig.node.json deleted file mode 100644 index 5104571..0000000 --- a/portal/tsconfig.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2023", - "lib": ["ES2023"], - "module": "ESNext", - "types": ["node"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - }, - "include": ["vite.config.ts"] -} diff --git a/portal/vite.config.ts b/portal/vite.config.ts deleted file mode 100644 index 76b917d..0000000 --- a/portal/vite.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {defineConfig} from 'vite'; -import react from '@vitejs/plugin-react'; -import tailwindcss from '@tailwindcss/vite'; -import path from 'path'; - -export default defineConfig(({command}) => ({ - base: command === 'serve' ? '/' : '/theme-assets/', - plugins: [react(), tailwindcss()], - publicDir: path.resolve(__dirname, 'public'), - resolve: {alias: {'@': path.resolve(__dirname, './src')}}, - build: { - outDir: path.resolve(__dirname, 'dist'), - emptyOutDir: true, - assetsDir: 'assets', - }, - server: { - port: 5173, - proxy: {'/api/': {target: 'http://localhost:8080/', changeOrigin: true}}, - }, -})); diff --git a/portal/yarn.lock b/portal/yarn.lock deleted file mode 100644 index d802cd4..0000000 --- a/portal/yarn.lock +++ /dev/null @@ -1,1384 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": - version "4.10.1" - resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz" - integrity sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg== - dependencies: - eslint-visitor-keys "^3.4.3" - -"@eslint-community/regexpp@^4.12.2": - version "4.12.2" - resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" - integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== - -"@eslint/config-array@^0.23.5": - version "0.23.5" - resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz" - integrity sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA== - dependencies: - "@eslint/object-schema" "^3.0.5" - debug "^4.3.1" - minimatch "^10.2.4" - -"@eslint/config-helpers@^0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz" - integrity sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw== - dependencies: - "@eslint/core" "^1.2.1" - -"@eslint/core@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz" - integrity sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/object-schema@^3.0.5": - version "3.0.5" - resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz" - integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw== - -"@eslint/plugin-kit@^0.7.2": - version "0.7.2" - resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz" - integrity sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A== - dependencies: - "@eslint/core" "^1.2.1" - levn "^0.4.1" - -"@floating-ui/core@^1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz" - integrity sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ== - dependencies: - "@floating-ui/utils" "^0.2.12" - -"@floating-ui/dom@^1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz" - integrity sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg== - dependencies: - "@floating-ui/core" "^1.8.0" - "@floating-ui/utils" "^0.2.12" - -"@floating-ui/react-dom@^2.0.0": - version "2.1.9" - resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz" - integrity sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg== - dependencies: - "@floating-ui/dom" "^1.8.0" - -"@floating-ui/utils@^0.2.12": - version "0.2.12" - resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz" - integrity sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww== - -"@humanfs/core@^0.19.2": - version "0.19.2" - resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz" - integrity sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA== - dependencies: - "@humanfs/types" "^0.15.0" - -"@humanfs/node@^0.16.6": - version "0.16.8" - resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz" - integrity sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ== - dependencies: - "@humanfs/core" "^0.19.2" - "@humanfs/types" "^0.15.0" - "@humanwhocodes/retry" "^0.4.0" - -"@humanfs/types@^0.15.0": - version "0.15.0" - resolved "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz" - integrity sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q== - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": - version "0.4.3" - resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" - integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.13" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" - integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/remapping@^2.3.5": - version "2.3.5" - resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" - integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": - version "1.5.5" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== - -"@jridgewell/trace-mapping@^0.3.24": - version "0.3.31" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" - integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@oxc-project/types@=0.139.0": - version "0.139.0" - resolved "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz" - integrity sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw== - -"@radix-ui/primitive@1.1.7": - version "1.1.7" - resolved "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz" - integrity sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q== - -"@radix-ui/react-arrow@1.1.15": - version "1.1.15" - resolved "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz" - integrity sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA== - dependencies: - "@radix-ui/react-primitive" "2.1.10" - -"@radix-ui/react-compose-refs@1.1.5": - version "1.1.5" - resolved "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz" - integrity sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA== - -"@radix-ui/react-context@1.2.2": - version "1.2.2" - resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz" - integrity sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA== - -"@radix-ui/react-dismissable-layer@1.1.19": - version "1.1.19" - resolved "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz" - integrity sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w== - dependencies: - "@radix-ui/primitive" "1.1.7" - "@radix-ui/react-compose-refs" "1.1.5" - "@radix-ui/react-primitive" "2.1.10" - "@radix-ui/react-use-callback-ref" "1.1.4" - "@radix-ui/react-use-effect-event" "0.0.5" - -"@radix-ui/react-id@1.1.4": - version "1.1.4" - resolved "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz" - integrity sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA== - dependencies: - "@radix-ui/react-use-layout-effect" "1.1.4" - -"@radix-ui/react-popper@1.3.7": - version "1.3.7" - resolved "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz" - integrity sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg== - dependencies: - "@floating-ui/react-dom" "^2.0.0" - "@radix-ui/react-arrow" "1.1.15" - "@radix-ui/react-compose-refs" "1.1.5" - "@radix-ui/react-context" "1.2.2" - "@radix-ui/react-primitive" "2.1.10" - "@radix-ui/react-use-callback-ref" "1.1.4" - "@radix-ui/react-use-layout-effect" "1.1.4" - "@radix-ui/react-use-rect" "1.1.4" - "@radix-ui/react-use-size" "1.1.4" - "@radix-ui/rect" "1.1.3" - -"@radix-ui/react-portal@1.1.17": - version "1.1.17" - resolved "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz" - integrity sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ== - dependencies: - "@radix-ui/react-primitive" "2.1.10" - "@radix-ui/react-use-layout-effect" "1.1.4" - -"@radix-ui/react-presence@1.1.10": - version "1.1.10" - resolved "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz" - integrity sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw== - dependencies: - "@radix-ui/react-use-layout-effect" "1.1.4" - -"@radix-ui/react-primitive@2.1.10": - version "2.1.10" - resolved "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz" - integrity sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg== - dependencies: - "@radix-ui/react-slot" "1.3.3" - -"@radix-ui/react-slot@1.3.3": - version "1.3.3" - resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz" - integrity sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q== - dependencies: - "@radix-ui/react-compose-refs" "1.1.5" - -"@radix-ui/react-tooltip@^1.2.12": - version "1.2.16" - resolved "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz" - integrity sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg== - dependencies: - "@radix-ui/primitive" "1.1.7" - "@radix-ui/react-compose-refs" "1.1.5" - "@radix-ui/react-context" "1.2.2" - "@radix-ui/react-dismissable-layer" "1.1.19" - "@radix-ui/react-id" "1.1.4" - "@radix-ui/react-popper" "1.3.7" - "@radix-ui/react-portal" "1.1.17" - "@radix-ui/react-presence" "1.1.10" - "@radix-ui/react-primitive" "2.1.10" - "@radix-ui/react-slot" "1.3.3" - "@radix-ui/react-use-controllable-state" "1.2.6" - "@radix-ui/react-use-layout-effect" "1.1.4" - "@radix-ui/react-visually-hidden" "1.2.11" - -"@radix-ui/react-use-callback-ref@1.1.4": - version "1.1.4" - resolved "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz" - integrity sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ== - -"@radix-ui/react-use-controllable-state@1.2.6": - version "1.2.6" - resolved "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz" - integrity sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ== - dependencies: - "@radix-ui/primitive" "1.1.7" - "@radix-ui/react-use-effect-event" "0.0.5" - "@radix-ui/react-use-layout-effect" "1.1.4" - -"@radix-ui/react-use-effect-event@0.0.5": - version "0.0.5" - resolved "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz" - integrity sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg== - dependencies: - "@radix-ui/react-use-layout-effect" "1.1.4" - -"@radix-ui/react-use-layout-effect@1.1.4": - version "1.1.4" - resolved "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz" - integrity sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw== - -"@radix-ui/react-use-rect@1.1.4": - version "1.1.4" - resolved "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz" - integrity sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ== - dependencies: - "@radix-ui/rect" "1.1.3" - -"@radix-ui/react-use-size@1.1.4": - version "1.1.4" - resolved "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz" - integrity sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw== - dependencies: - "@radix-ui/react-use-layout-effect" "1.1.4" - -"@radix-ui/react-visually-hidden@1.2.11": - version "1.2.11" - resolved "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz" - integrity sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ== - dependencies: - "@radix-ui/react-primitive" "2.1.10" - -"@radix-ui/rect@1.1.3": - version "1.1.3" - resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz" - integrity sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw== - -"@reduxjs/toolkit@^1.9.0 || 2.x.x": - version "2.12.0" - resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz" - integrity sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw== - dependencies: - "@standard-schema/spec" "^1.0.0" - "@standard-schema/utils" "^0.3.0" - immer "^11.0.0" - redux "^5.0.1" - redux-thunk "^3.1.0" - reselect "^5.1.0" - -"@rolldown/binding-darwin-arm64@1.1.5": - version "1.1.5" - resolved "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz" - integrity sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw== - -"@rolldown/pluginutils@^1.0.0", "@rolldown/pluginutils@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz" - integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw== - -"@standard-schema/spec@^1.0.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz" - integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== - -"@standard-schema/utils@^0.3.0": - version "0.3.0" - resolved "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz" - integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== - -"@tailwindcss/node@4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz" - integrity sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg== - dependencies: - "@jridgewell/remapping" "^2.3.5" - enhanced-resolve "^5.24.1" - jiti "^2.7.0" - lightningcss "1.32.0" - magic-string "^0.30.21" - source-map-js "^1.2.1" - tailwindcss "4.3.3" - -"@tailwindcss/oxide-darwin-arm64@4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz" - integrity sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw== - -"@tailwindcss/oxide@4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz" - integrity sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA== - optionalDependencies: - "@tailwindcss/oxide-android-arm64" "4.3.3" - "@tailwindcss/oxide-darwin-arm64" "4.3.3" - "@tailwindcss/oxide-darwin-x64" "4.3.3" - "@tailwindcss/oxide-freebsd-x64" "4.3.3" - "@tailwindcss/oxide-linux-arm-gnueabihf" "4.3.3" - "@tailwindcss/oxide-linux-arm64-gnu" "4.3.3" - "@tailwindcss/oxide-linux-arm64-musl" "4.3.3" - "@tailwindcss/oxide-linux-x64-gnu" "4.3.3" - "@tailwindcss/oxide-linux-x64-musl" "4.3.3" - "@tailwindcss/oxide-wasm32-wasi" "4.3.3" - "@tailwindcss/oxide-win32-arm64-msvc" "4.3.3" - "@tailwindcss/oxide-win32-x64-msvc" "4.3.3" - -"@tailwindcss/vite@^4.3.2": - version "4.3.3" - resolved "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz" - integrity sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw== - dependencies: - "@tailwindcss/node" "4.3.3" - "@tailwindcss/oxide" "4.3.3" - tailwindcss "4.3.3" - -"@tanstack/query-core@5.101.4": - version "5.101.4" - resolved "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz" - integrity sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw== - -"@tanstack/react-query@^5.101.2": - version "5.101.4" - resolved "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz" - integrity sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA== - dependencies: - "@tanstack/query-core" "5.101.4" - -"@types/d3-array@^3.0.3": - version "3.2.2" - resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz" - integrity sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw== - -"@types/d3-color@*": - version "3.1.3" - resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz" - integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== - -"@types/d3-ease@^3.0.0": - version "3.0.2" - resolved "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz" - integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== - -"@types/d3-interpolate@^3.0.1": - version "3.0.4" - resolved "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz" - integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== - dependencies: - "@types/d3-color" "*" - -"@types/d3-path@*": - version "3.1.1" - resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz" - integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== - -"@types/d3-scale@^4.0.2": - version "4.0.9" - resolved "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz" - integrity sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== - dependencies: - "@types/d3-time" "*" - -"@types/d3-shape@^3.1.0": - version "3.1.8" - resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz" - integrity sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w== - dependencies: - "@types/d3-path" "*" - -"@types/d3-time@*", "@types/d3-time@^3.0.0": - version "3.0.4" - resolved "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz" - integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== - -"@types/d3-timer@^3.0.0": - version "3.0.2" - resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz" - integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== - -"@types/esrecurse@^4.3.1": - version "4.3.1" - resolved "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz" - integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== - -"@types/estree@^1.0.6", "@types/estree@^1.0.8": - version "1.0.9" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz" - integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== - -"@types/json-schema@^7.0.15": - version "7.0.15" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/node@^20.19.0 || >=22.12.0", "@types/node@^26.1.1": - version "26.1.2" - resolved "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz" - integrity sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg== - dependencies: - undici-types "~8.3.0" - -"@types/react-dom@*", "@types/react-dom@^19.2.3": - version "19.2.4" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz" - integrity sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw== - -"@types/react@*", "@types/react@^18.2.25 || ^19", "@types/react@^19.2.0", "@types/react@^19.2.17": - version "19.2.18" - resolved "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz" - integrity sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w== - dependencies: - csstype "^3.2.2" - -"@types/use-sync-external-store@^0.0.6": - version "0.0.6" - resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz" - integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== - -"@typescript-eslint/eslint-plugin@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz" - integrity sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA== - dependencies: - "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.65.0" - "@typescript-eslint/type-utils" "8.65.0" - "@typescript-eslint/utils" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - ignore "^7.0.5" - natural-compare "^1.4.0" - ts-api-utils "^2.5.0" - -"@typescript-eslint/parser@^8.65.0", "@typescript-eslint/parser@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz" - integrity sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA== - dependencies: - "@typescript-eslint/scope-manager" "8.65.0" - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - debug "^4.4.3" - -"@typescript-eslint/project-service@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz" - integrity sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q== - dependencies: - "@typescript-eslint/tsconfig-utils" "^8.65.0" - "@typescript-eslint/types" "^8.65.0" - debug "^4.4.3" - -"@typescript-eslint/scope-manager@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz" - integrity sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg== - dependencies: - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - -"@typescript-eslint/tsconfig-utils@^8.65.0", "@typescript-eslint/tsconfig-utils@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz" - integrity sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg== - -"@typescript-eslint/type-utils@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz" - integrity sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g== - dependencies: - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - "@typescript-eslint/utils" "8.65.0" - debug "^4.4.3" - ts-api-utils "^2.5.0" - -"@typescript-eslint/types@^8.65.0", "@typescript-eslint/types@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz" - integrity sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg== - -"@typescript-eslint/typescript-estree@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz" - integrity sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg== - dependencies: - "@typescript-eslint/project-service" "8.65.0" - "@typescript-eslint/tsconfig-utils" "8.65.0" - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - debug "^4.4.3" - minimatch "^10.2.2" - semver "^7.7.3" - tinyglobby "^0.2.15" - ts-api-utils "^2.5.0" - -"@typescript-eslint/utils@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz" - integrity sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA== - dependencies: - "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.65.0" - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - -"@typescript-eslint/visitor-keys@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz" - integrity sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A== - dependencies: - "@typescript-eslint/types" "8.65.0" - eslint-visitor-keys "^5.0.0" - -"@vitejs/plugin-react@^6.0.3": - version "6.0.5" - resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz" - integrity sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA== - dependencies: - "@rolldown/pluginutils" "^1.0.1" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.16.0: - version "8.18.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz" - integrity sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ== - -ajv@^6.14.0: - version "6.15.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz" - integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -balanced-match@^4.0.2: - version "4.0.4" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" - integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== - -brace-expansion@^5.0.8: - version "5.0.9" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz" - integrity sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg== - dependencies: - balanced-match "^4.0.2" - -clsx@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" - integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== - -cookie@^1.0.1: - version "1.1.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz" - integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -csstype@^3.2.2: - version "3.2.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" - integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== - -d3-array@^3.1.6, "d3-array@2 - 3", "d3-array@2.10.0 - 3": - version "3.2.4" - resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz" - integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== - dependencies: - internmap "1 - 2" - -"d3-color@1 - 3": - version "3.1.0" - resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz" - integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== - -d3-ease@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz" - integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== - -"d3-format@1 - 3": - version "3.1.2" - resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz" - integrity sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg== - -d3-interpolate@^3.0.1, "d3-interpolate@1.2.0 - 3": - version "3.0.1" - resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz" - integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== - dependencies: - d3-color "1 - 3" - -d3-path@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz" - integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== - -d3-scale@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz" - integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== - dependencies: - d3-array "2.10.0 - 3" - d3-format "1 - 3" - d3-interpolate "1.2.0 - 3" - d3-time "2.1.1 - 3" - d3-time-format "2 - 4" - -d3-shape@^3.1.0: - version "3.2.0" - resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz" - integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== - dependencies: - d3-path "^3.1.0" - -"d3-time-format@2 - 4": - version "4.1.0" - resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz" - integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== - dependencies: - d3-time "1 - 3" - -d3-time@^3.0.0, "d3-time@1 - 3", "d3-time@2.1.1 - 3": - version "3.1.0" - resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz" - integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== - dependencies: - d3-array "2 - 3" - -d3-timer@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz" - integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== - -dayjs@^1.11.21: - version "1.11.21" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz" - integrity sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA== - -debug@^4.3.1, debug@^4.3.2, debug@^4.4.3: - version "4.4.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - -decimal.js-light@^2.5.1: - version "2.5.1" - resolved "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz" - integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -detect-libc@^2.0.3: - version "2.1.2" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" - integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== - -enhanced-resolve@^5.24.1: - version "5.24.5" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz" - integrity sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.3.3" - -es-toolkit@^1.39.3: - version "1.50.0" - resolved "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz" - integrity sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@^9.1.2: - version "9.1.2" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz" - integrity sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ== - dependencies: - "@types/esrecurse" "^4.3.1" - "@types/estree" "^1.0.8" - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz" - integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== - -eslint@^10.7.0, "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0 || ^10.0.0": - version "10.8.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz" - integrity sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ== - dependencies: - "@eslint-community/eslint-utils" "^4.8.0" - "@eslint-community/regexpp" "^4.12.2" - "@eslint/config-array" "^0.23.5" - "@eslint/config-helpers" "^0.7.0" - "@eslint/core" "^1.2.1" - "@eslint/plugin-kit" "^0.7.2" - "@humanfs/node" "^0.16.6" - "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.2" - "@types/estree" "^1.0.6" - ajv "^6.14.0" - cross-spawn "^7.0.6" - debug "^4.3.2" - escape-string-regexp "^4.0.0" - eslint-scope "^9.1.2" - eslint-visitor-keys "^5.0.1" - espree "^11.2.0" - esquery "^1.7.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" - minimatch "^10.2.5" - natural-compare "^1.4.0" - optionator "^0.9.3" - -espree@^11.2.0: - version "11.2.0" - resolved "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz" - integrity sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw== - dependencies: - acorn "^8.16.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^5.0.1" - -esquery@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz" - integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -eventemitter3@^5.0.1: - version "5.0.4" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz" - integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fdir@^6.5.0: - version "6.5.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" - integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== - -file-entry-cache@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" - integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== - dependencies: - flat-cache "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" - integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.4" - -flatted@^3.2.9: - version "3.4.4" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz" - integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q== - -fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -globals@^17.7.0: - version "17.8.0" - resolved "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz" - integrity sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ== - -graceful-fs@^4.2.4: - version "4.2.11" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -ignore@^5.2.0: - version "5.3.2" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" - integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== - -ignore@^7.0.5: - version "7.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz" - integrity sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw== - -immer@^11.0.0, immer@^11.1.8: - version "11.1.15" - resolved "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz" - integrity sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ== - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -"internmap@1 - 2": - version "2.0.3" - resolved "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz" - integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-glob@^4.0.0, is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -jiti@*, jiti@^2.7.0, jiti@>=1.21.0: - version "2.7.0" - resolved "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz" - integrity sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -keyv@^4.5.4: - version "4.5.4" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lightningcss-darwin-arm64@1.32.0: - version "1.32.0" - resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz" - integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ== - -lightningcss@^1.32.0, lightningcss@1.32.0: - version "1.32.0" - resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz" - integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ== - dependencies: - detect-libc "^2.0.3" - optionalDependencies: - lightningcss-android-arm64 "1.32.0" - lightningcss-darwin-arm64 "1.32.0" - lightningcss-darwin-x64 "1.32.0" - lightningcss-freebsd-x64 "1.32.0" - lightningcss-linux-arm-gnueabihf "1.32.0" - lightningcss-linux-arm64-gnu "1.32.0" - lightningcss-linux-arm64-musl "1.32.0" - lightningcss-linux-x64-gnu "1.32.0" - lightningcss-linux-x64-musl "1.32.0" - lightningcss-win32-arm64-msvc "1.32.0" - lightningcss-win32-x64-msvc "1.32.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lucide-react@^1.24.0: - version "1.28.0" - resolved "https://registry.npmjs.org/lucide-react/-/lucide-react-1.28.0.tgz" - integrity sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg== - -magic-string@^0.30.21: - version "0.30.21" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz" - integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.5" - -minimatch@^10.2.2, minimatch@^10.2.4, minimatch@^10.2.5: - version "10.2.6" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz" - integrity sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A== - dependencies: - brace-expansion "^5.0.8" - -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@^3.3.16: - version "3.3.16" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz" - integrity sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -optionator@^0.9.3: - version "0.9.4" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" - integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.5" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -"picomatch@^3 || ^4", picomatch@^4.0.4, picomatch@^4.0.5: - version "4.0.5" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz" - integrity sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A== - -postcss@^8.5.16: - version "8.5.25" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz" - integrity sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw== - dependencies: - nanoid "^3.3.16" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -"react-dom@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", react-dom@^19.2.7, react-dom@>=16.8.0, react-dom@>=18: - version "19.2.8" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz" - integrity sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ== - dependencies: - scheduler "^0.27.0" - -"react-is@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": - version "19.2.8" - resolved "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz" - integrity sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ== - -"react-redux@^7.2.1 || ^8.1.3 || ^9.0.0", "react-redux@8.x.x || 9.x.x": - version "9.3.0" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz" - integrity sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g== - dependencies: - "@types/use-sync-external-store" "^0.0.6" - use-sync-external-store "^1.4.0" - -react-router-dom@^7.18.1: - version "7.18.2" - resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz" - integrity sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw== - dependencies: - react-router "7.18.2" - -react-router@7.18.2: - version "7.18.2" - resolved "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz" - integrity sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg== - dependencies: - cookie "^1.0.1" - set-cookie-parser "^2.6.0" - -"react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^18 || ^19", "react@^18.0 || ^19", react@^19.2.7, react@^19.2.8, react@>=16.8.0, react@>=18: - version "19.2.8" - resolved "https://registry.npmjs.org/react/-/react-19.2.8.tgz" - integrity sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw== - -recharts@^3.9.2: - version "3.10.1" - resolved "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz" - integrity sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA== - dependencies: - "@reduxjs/toolkit" "^1.9.0 || 2.x.x" - clsx "^2.1.1" - decimal.js-light "^2.5.1" - es-toolkit "^1.39.3" - eventemitter3 "^5.0.1" - immer "^11.1.8" - react-redux "8.x.x || 9.x.x" - reselect "5.2.0" - tiny-invariant "^1.3.3" - use-sync-external-store "^1.2.2" - victory-vendor "^37.0.2" - -redux-thunk@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz" - integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== - -redux@^5.0.0, redux@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz" - integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== - -reselect@^5.1.0, reselect@5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz" - integrity sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw== - -rolldown@~1.1.4: - version "1.1.5" - resolved "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz" - integrity sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA== - dependencies: - "@oxc-project/types" "=0.139.0" - "@rolldown/pluginutils" "^1.0.0" - optionalDependencies: - "@rolldown/binding-android-arm64" "1.1.5" - "@rolldown/binding-darwin-arm64" "1.1.5" - "@rolldown/binding-darwin-x64" "1.1.5" - "@rolldown/binding-freebsd-x64" "1.1.5" - "@rolldown/binding-linux-arm-gnueabihf" "1.1.5" - "@rolldown/binding-linux-arm64-gnu" "1.1.5" - "@rolldown/binding-linux-arm64-musl" "1.1.5" - "@rolldown/binding-linux-ppc64-gnu" "1.1.5" - "@rolldown/binding-linux-s390x-gnu" "1.1.5" - "@rolldown/binding-linux-x64-gnu" "1.1.5" - "@rolldown/binding-linux-x64-musl" "1.1.5" - "@rolldown/binding-openharmony-arm64" "1.1.5" - "@rolldown/binding-wasm32-wasi" "1.1.5" - "@rolldown/binding-win32-arm64-msvc" "1.1.5" - "@rolldown/binding-win32-x64-msvc" "1.1.5" - -scheduler@^0.27.0: - version "0.27.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" - integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== - -semver@^7.7.3: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -set-cookie-parser@^2.6.0: - version "2.7.2" - resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz" - integrity sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -tailwind-merge@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz" - integrity sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w== - -tailwindcss@4.3.3: - version "4.3.3" - resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz" - integrity sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ== - -tapable@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz" - integrity sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A== - -tiny-invariant@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz" - integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== - -tinyglobby@^0.2.15, tinyglobby@^0.2.17: - version "0.2.17" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz" - integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g== - dependencies: - fdir "^6.5.0" - picomatch "^4.0.4" - -ts-api-utils@^2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz" - integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== - -tw-animate-css@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz" - integrity sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ== - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -typescript-eslint@^8.63.0: - version "8.65.0" - resolved "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz" - integrity sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA== - dependencies: - "@typescript-eslint/eslint-plugin" "8.65.0" - "@typescript-eslint/parser" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - "@typescript-eslint/utils" "8.65.0" - -typescript@>=4.8.4, "typescript@>=4.8.4 <6.1.0", typescript@~6.0.3: - version "6.0.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz" - integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== - -undici-types@~8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz" - integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0: - version "1.6.0" - resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz" - integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== - -victory-vendor@^37.0.2: - version "37.3.6" - resolved "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz" - integrity sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ== - dependencies: - "@types/d3-array" "^3.0.3" - "@types/d3-ease" "^3.0.0" - "@types/d3-interpolate" "^3.0.1" - "@types/d3-scale" "^4.0.2" - "@types/d3-shape" "^3.1.0" - "@types/d3-time" "^3.0.0" - "@types/d3-timer" "^3.0.0" - d3-array "^3.1.6" - d3-ease "^3.0.1" - d3-interpolate "^3.0.1" - d3-scale "^4.0.2" - d3-shape "^3.1.0" - d3-time "^3.0.0" - d3-timer "^3.0.1" - -"vite@^5.2.0 || ^6 || ^7 || ^8", vite@^8.0.0, vite@8.1.4: - version "8.1.4" - resolved "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz" - integrity sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ== - dependencies: - lightningcss "^1.32.0" - picomatch "^4.0.5" - postcss "^8.5.16" - rolldown "~1.1.4" - tinyglobby "^0.2.17" - optionalDependencies: - fsevents "~2.3.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/web/README.md b/web/README.md index a84d477..4b2203c 100644 --- a/web/README.md +++ b/web/README.md @@ -2,24 +2,35 @@ `web/` 是官方管理后台前端(React/Vite),发布到 `/admin/assets/*`。 -官方默认公开主题在仓库根目录的 `portal/`,是一个独立的项目,可以搬迁到单独的仓库。 +官方默认公开主题是独立项目 [`pika-monitor/pika-default-theme`](https://github.com/pika-monitor/pika-default-theme)。本地开发时,`Makefile` 默认从同级目录 `../pika-default-theme` 构建它。 ## 开发 ```bash -cd web && npm ci && npm run dev # 管理后台,http://localhost:5174/admin/ -cd portal && npm ci && npm run dev # 默认主题,http://localhost:5173/ +cd web && npm ci && npm run dev # 管理后台,http://localhost:5174/admin/ +cd ../pika-default-theme && npm ci && npm run dev # 默认主题,http://localhost:5173/ ``` 两者都把 `/api/*` 代理到 `http://localhost:8080`,可以同时启动。 ## 构建 -`make build-web` 分别构建 `web/`(管理后台 → `web/dist/admin/`)和 `portal/`(默认主题 → `portal/dist/`)。后端直接从这两个目录读取,无需组装脚本。 +`make build-web` 构建 `web/` 和独立的默认主题,将两者放到独立的发布目录: + +- 管理后台:`web/dist/`; +- 默认主题:`themes/default/`。 + +如果主题源码不在默认位置,可以显式指定: + +```bash +make DEFAULT_THEME_DIR=/path/to/pika-default-theme build-web +``` + +GitHub Actions 使用 `.github/default-theme.ref` 中的完整 Git Commit SHA 锁定默认主题,保证同一 Pika 版本的发布构建可复现。 ## 运行路径 - 公开主题:`/`、`/servers/*`、`/monitors/*`; - 管理后台:`/admin/*`,包括 `/admin/login` 和 OAuth/OIDC 回调; - 管理后台资源:`/admin/assets/*`; -- 活动主题资源:`/theme-assets/*`。 +- 活动主题资源:`/t/*`。 diff --git a/web/vite.config.ts b/web/vite.config.ts index ca4e335..40bc43f 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }}, build: { - outDir: path.resolve(__dirname, 'dist/admin'), + outDir: path.resolve(__dirname, 'dist'), emptyOutDir: true, assetsDir: 'assets', },