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;
-}) => (
-
-);
-
-/* ========================================== 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 (
- <>
-
-
- {/* 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 (
-
- );
-};
-
-/* ========================================== 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 (
-
- );
-};
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 && (
-
- )}
-
- );
- })}
-
-
- {/* 桌面端表格布局 */}
-
-
-
-
- |
- 探针名称
- |
-
- 状态
- |
-
- 响应时间
- |
-
- 最后检测
- |
- {monitorType === 'https' && (
-
- 证书信息
- |
- )}
-
- 错误信息
- |
-
-
-
- {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 ? (
-
- ) : (
- -
- )}
- |
-
- );
- })}
-
-
-
-
- );
-};
-
-/* ========================================== 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.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.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 ? (
-
- ) : (
- <>
- {/* 桌面端表格布局 */}
-
-
-
-
- | Identity |
- Telemetry |
- I/O Rate |
- Traffic |
- Network |
- Meta / Tags |
-
-
-
- {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 */}
-
-
-
-
- {server.name}
-
-
- {server.os}
-
- {server.arch}
-
- {isOnline && server.metrics?.host && (
-
-
-
- {formatUptime(server.metrics.host.uptime)}
-
-
-
- {server.metrics.host.procs} 进程
-
-
- )}
-
-
- |
-
- {/* Resources */}
-
- {isOnline ? (
-
-
-
-
- {temperatures.length > 0 && (
-
-
- {temperatures.map((temp, index) => (
-
- {temp.temperature?.toFixed(1)}°C
- {temp.type}
- {index < temperatures.length - 1 &&
- |}
-
- ))}
-
- )}
-
- ) : (
-
-
- CONNECTION_LOST // RECONNECTING...
-
- )}
- |
-
- {/* Network */}
-
-
-
-
- {formatSpeed(download)}
-
-
-
- {formatSpeed(upload)}
-
-
- |
-
- {/* Traffic */}
-
- {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)}
- 仅统计模式
-
- )}
-
- ) : (
- -
- )}
- |
-
- {/* Connections */}
-
- {isOnline && netConn ? (
-
-
-
- {netConn.established || 0}
- ESTABLISHED
-
-
-
- {netConn.listen || 0}
- LISTEN
-
-
-
- {netConn.closeWait || 0}
- CLOSE_WAIT
-
-
- ) : (
- -
- )}
- |
-
- {/* Meta */}
-
-
-
- {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',
},