commit 0bb423fcca3d9cbd1a8340cfbdfbe900f2f2470b Author: abearxiong Date: Tue Oct 21 18:29:15 2025 +0800 Initial commit: restore project after Git corruption diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d89ebd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules + +dist + +.turbo + +filebrowser + +filebrowser.db + +/data \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3ffa645 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM oven/bun:alpine +# From bun:latest + +# 创建应用目录 +WORKDIR /app + +COPY server/package.json ./ + +# 复制源码 +COPY ./server ./ + +COPY ./server/code ./code-backup + +COPY ./web/dist ./demo/root/light-code-center +RUN bun install + +# 构建(可选) +# RUN bun run build + +# 暴露端口 +EXPOSE 4005 + +# 启动服务 +CMD ["bun", "start"] + +# 保持容器运行 +# CMD ["tail", "-f", "/dev/null"] \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..e614808 --- /dev/null +++ b/compose.yml @@ -0,0 +1,24 @@ +services: + light-code: + # image: 'kevisual/light-code:latest' + image: 'crpi-92z54xpbq1hzmdcz.cn-hangzhou.personal.cr.aliyuncs.com/kevisual/light-code:latest' + user: root + volumes: + - ./data/code:/app/code + ports: + - '6005:4005' + restart: always + + filebrowser: + image: 'filebrowser/filebrowser' + user: root + volumes: + - ./data/code:/srv + - ./data/database:/database + - ./data/config:/config + restart: unless-stopped + ports: + - '6006:80' + environment: + - PUID=1000 + - PGID=1000 \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..69d092d --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "@kevisual/light-code", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "dev": "pnpm run dev:server & pnpm run dev:web", + "dev:server": "cd server && pnpm dev", + "dev:web": "cd web && pnpm dev", + "docker:build": "docker build -t kevisual/light-code:latest .", + "docker:aliyun": "docker build -t crpi-92z54xpbq1hzmdcz.cn-hangzhou.personal.cr.aliyuncs.com/kevisual/light-code .", + "docker:dev": "docker compose up", + "build": "turbo build" + }, + "keywords": [], + "author": "abearxiong (https://www.xiongxiao.me)", + "license": "MIT", + "packageManager": "pnpm@10.18.3", + "type": "module", + "dependencies": { + "turbo": "^2.5.8" + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..8a3842d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6706 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + turbo: + specifier: ^2.5.8 + version: 2.5.8 + + server: + dependencies: + '@kevisual/noco': + specifier: ^0.0.1 + version: 0.0.1 + '@kevisual/query': + specifier: ^0.0.29 + version: 0.0.29(zod@3.25.76) + '@kevisual/router': + specifier: ^0.0.29 + version: 0.0.29 + fast-glob: + specifier: ^3.3.3 + version: 3.3.3 + pocketbase: + specifier: ^0.26.2 + version: 0.26.2 + unstorage: + specifier: ^1.17.1 + version: 1.17.1(idb-keyval@6.2.2) + devDependencies: + '@kevisual/local-proxy': + specifier: ^0.0.6 + version: 0.0.6 + '@kevisual/types': + specifier: ^0.0.10 + version: 0.0.10 + '@kevisual/use-config': + specifier: ^1.0.19 + version: 1.0.19(dotenv@17.2.3) + '@types/bun': + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.2) + + web: + dependencies: + '@astrojs/mdx': + specifier: ^4.3.7 + version: 4.3.7(astro@5.14.5(@types/node@24.7.2)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.52.4)(typescript@5.9.3)) + '@astrojs/react': + specifier: ^4.4.0 + version: 4.4.0(@types/node@24.7.2)(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@astrojs/sitemap': + specifier: ^3.6.0 + version: 3.6.0 + '@faker-js/faker': + specifier: ^10.1.0 + version: 10.1.0 + '@floating-ui/react': + specifier: ^0.27.16 + version: 0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@kevisual/noco': + specifier: ^0.0.1 + version: 0.0.1 + '@kevisual/query': + specifier: ^0.0.29 + version: 0.0.29(zod@3.25.76) + '@kevisual/query-login': + specifier: ^0.0.6 + version: 0.0.6(@kevisual/query@0.0.29(zod@3.25.76))(rollup@4.52.4)(tslib@2.8.1)(typescript@5.9.3) + '@kevisual/registry': + specifier: ^0.0.1 + version: 0.0.1(typescript@5.9.3) + '@ricky0123/vad-web': + specifier: ^0.0.28 + version: 0.0.28 + '@szhsin/react-menu': + specifier: ^4.5.0 + version: 4.5.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tailwindcss/vite': + specifier: ^4.1.14 + version: 4.1.14(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)) + astro: + specifier: ^5.14.4 + version: 5.14.5(@types/node@24.7.2)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.52.4)(typescript@5.9.3) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + dayjs: + specifier: ^1.11.18 + version: 1.11.18 + es-toolkit: + specifier: ^1.40.0 + version: 1.40.0 + events: + specifier: ^3.3.0 + version: 3.3.0 + graphology: + specifier: ^0.26.0 + version: 0.26.0(graphology-types@0.24.8) + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + lucide-react: + specifier: ^0.545.0 + version: 0.545.0(react@19.2.0) + marked: + specifier: ^16.4.1 + version: 16.4.1 + nanoid: + specifier: ^5.1.6 + version: 5.1.6 + pocketbase: + specifier: ^0.26.2 + version: 0.26.2 + pouchdb-adapter-memory: + specifier: ^9.0.0 + version: 9.0.0 + pouchdb-browser: + specifier: ^9.0.0 + version: 9.0.0 + react: + specifier: ^19.2.0 + version: 19.2.0 + react-dom: + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) + react-modal: + specifier: ^3.16.3 + version: 3.16.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react-resizable-panels: + specifier: ^3.0.6 + version: 3.0.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react-toastify: + specifier: ^11.0.5 + version: 11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react-virtualized: + specifier: ^9.22.6 + version: 9.22.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + sigma: + specifier: ^3.0.2 + version: 3.0.2(graphology-types@0.24.8) + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 + three: + specifier: ^0.180.0 + version: 0.180.0 + wavesurfer.js: + specifier: ^7.11.0 + version: 7.11.0 + zustand: + specifier: ^5.0.8 + version: 5.0.8(@types/react@19.2.2)(react@19.2.0) + devDependencies: + '@kevisual/types': + specifier: ^0.0.10 + version: 0.0.10 + '@types/pouchdb': + specifier: ^6.4.2 + version: 6.4.2 + '@types/pouchdb-browser': + specifier: ^6.1.5 + version: 6.1.5 + '@types/react': + specifier: ^19.2.2 + version: 19.2.2 + '@types/react-dom': + specifier: ^19.2.2 + version: 19.2.2(@types/react@19.2.2) + '@types/react-modal': + specifier: ^3.16.3 + version: 3.16.3 + '@types/react-virtualized': + specifier: ^9.22.3 + version: 9.22.3 + '@types/three': + specifier: ^0.180.0 + version: 0.180.0 + '@vitejs/plugin-basic-ssl': + specifier: ^2.1.0 + version: 2.1.0(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)) + dotenv: + specifier: ^17.2.3 + version: 17.2.3 + pouchdb: + specifier: ^9.0.0 + version: 9.0.0 + tailwindcss: + specifier: ^4.1.14 + version: 4.1.14 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + +packages: + + '@astrojs/compiler@2.13.0': + resolution: {integrity: sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==} + + '@astrojs/internal-helpers@0.7.4': + resolution: {integrity: sha512-lDA9MqE8WGi7T/t2BMi+EAXhs4Vcvr94Gqx3q15cFEz8oFZMO4/SFBqYr/UcmNlvW+35alowkVj+w9VhLvs5Cw==} + + '@astrojs/markdown-remark@6.3.8': + resolution: {integrity: sha512-uFNyFWadnULWK2cOw4n0hLKeu+xaVWeuECdP10cQ3K2fkybtTlhb7J7TcScdjmS8Yps7oje9S/ehYMfZrhrgCg==} + + '@astrojs/mdx@4.3.7': + resolution: {integrity: sha512-5SRmvMyT/UMWaU2eoD+htnXtE2mUZZEH2K/nEzhuEy+iCsOSuS/DUry59WuKUJRQETi1mgJFdNR4dZLJHYVuRA==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + astro: ^5.0.0 + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/react@4.4.0': + resolution: {integrity: sha512-RzblkVImAFdV1C0AWsSWzS70Z0FMtW2p0XXkNYu3QePfyVJta3JIy8m8jY8271etaCZtpFjsE2UaiHGZIBm6nw==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + '@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0 + '@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + + '@astrojs/sitemap@3.6.0': + resolution: {integrity: sha512-4aHkvcOZBWJigRmMIAJwRQXBS+ayoP5z40OklTXYXhUDhwusz+DyDl+nSshY6y9DvkVEavwNcFO8FD81iGhXjg==} + + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + + '@capsizecss/unpack@3.0.0': + resolution: {integrity: sha512-+ntATQe1AlL7nTOYjwjj6w3299CgRot48wL761TUGYpYgAou3AaONZazp0PKZyCyWhudWsjhq1nvRHOvbMzhTA==} + engines: {node: '>=18'} + + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@faker-js/faker@10.1.0': + resolution: {integrity: sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg==} + engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.27.16': + resolution: {integrity: sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@kevisual/cache@0.0.2': + resolution: {integrity: sha512-2Cl5KF2Gi27uLfhO6CdTMFnRzx9vYnqevAo7d9ab3rOaqTgF8tLeAXglXyRbaWW3WUbHU2XaOb4r98uUsqIQQw==} + + '@kevisual/load@0.0.6': + resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} + + '@kevisual/local-proxy@0.0.6': + resolution: {integrity: sha512-FL6m84FGPLf+WtcNVfKtwcmOtY+iVb5VPEXlKPv2JzzZKLkuEzEjtm8iLAuawwfr+8/MONMt1L7M0dTTR0JcEw==} + + '@kevisual/noco@0.0.1': + resolution: {integrity: sha512-N0wYcxasxXe3S6jQtD5SSUwOOmaPaL+9S9Qe+iwA2oXYNUmheNXgz2vQJUmKa3SZHgOrwsKMMe3ZhAHftJizQg==, tarball: https://registry.npmjs.org/@kevisual/noco/-/noco-0.0.1.tgz} + + '@kevisual/query-login@0.0.6': + resolution: {integrity: sha512-ZdX+sxeQaM3PV9fZXofMlxFz1RmpYIkoi47exzUgw6DADjEryBAQKRXe2/oL20NsBTV8owqaagRqffAVjq5c5g==} + peerDependencies: + '@kevisual/query': ^0.0.17 + + '@kevisual/query@0.0.29': + resolution: {integrity: sha512-rQZk0J073UuC1QGzuyq+pb4Y0hu8/Qx/xYHs9NbsmslM+RuMnd1zpXmvhXNj7Kn1MdYTH90ng2MlFLBkkQFaIg==} + + '@kevisual/registry@0.0.1': + resolution: {integrity: sha512-//OHu9m4JDrMjgP8o8dcjZd3D3IAUkRVlkTSviouZEH7r5m7mccA3Hvzw0XJ/lelx6exC6LWsyv6c4uV0Dp+gw==} + + '@kevisual/router@0.0.29': + resolution: {integrity: sha512-UD2aWgf5yv/HmX3FOCPvvRLaZqYAP1JUsCgswo9BSibrVZYh7ZEnShYYnE+P2mr34UI2xEn+BJVxLMCLXg83sA==} + + '@kevisual/types@0.0.10': + resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==} + + '@kevisual/use-config@1.0.19': + resolution: {integrity: sha512-Q1IH4eMqUe5w6Bq8etoqOSls9FPIy0xwwD3wHf26EsQLZadhccI9qkDuFzP/rFWDa57mwFPEfwbGE5UlqWOCkw==} + peerDependencies: + dotenv: ^16.4.7 + + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@ricky0123/vad-web@0.0.28': + resolution: {integrity: sha512-Hvw8jN3r1SBxmjJa89HITxRcwlT6dc7CQPVtVQLrqfY8EeQcx41QeqKUol4lw8ZCeAIHKwYndHnB1K/4SAQJgQ==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/plugin-commonjs@28.0.7': + resolution: {integrity: sha512-6cE2Wr/MkpdtTS8gXlCn9Zdmf7e9Xm96yFqOwFEXuvYLAHtjRf57/n6GEVF4K8NSesT1eKdBtcDA/SQdpW/8nA==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-typescript@12.1.4': + resolution: {integrity: sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.52.4': + resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.52.4': + resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.52.4': + resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.52.4': + resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.52.4': + resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.52.4': + resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.52.4': + resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.52.4': + resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.52.4': + resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.52.4': + resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-gnu@4.52.4': + resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.52.4': + resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.52.4': + resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.52.4': + resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.52.4': + resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.52.4': + resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openharmony-arm64@4.52.4': + resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.4': + resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.52.4': + resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.4': + resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.4': + resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} + cpu: [x64] + os: [win32] + + '@shikijs/core@3.13.0': + resolution: {integrity: sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==} + + '@shikijs/engine-javascript@3.13.0': + resolution: {integrity: sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==} + + '@shikijs/engine-oniguruma@3.13.0': + resolution: {integrity: sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==} + + '@shikijs/langs@3.13.0': + resolution: {integrity: sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==} + + '@shikijs/themes@3.13.0': + resolution: {integrity: sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==} + + '@shikijs/types@3.13.0': + resolution: {integrity: sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@szhsin/react-menu@4.5.0': + resolution: {integrity: sha512-fblZBPxFGjg+QxSbdDsWk3H8brupuQG+ayYXElwg+FdCxwLQLvrHG9K6O9+4pE8qLyDy5REn/2HmffPXcBZviA==} + peerDependencies: + react: '>=16.14.0' + react-dom: '>=16.14.0' + + '@tailwindcss/node@4.1.14': + resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==} + + '@tailwindcss/oxide-android-arm64@4.1.14': + resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.14': + resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.14': + resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.14': + resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': + resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': + resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': + resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': + resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.1.14': + resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.1.14': + resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': + resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': + resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.14': + resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.14': + resolution: {integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/bun@1.3.0': + resolution: {integrity: sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/fontkit@2.0.8': + resolution: {integrity: sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + + '@types/node@24.7.2': + resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==} + + '@types/pouchdb-adapter-cordova-sqlite@1.0.4': + resolution: {integrity: sha512-1MGjmAMux3OIyJ+iXfhJ5hNIzS+KjGJ05O3bF5Gen5TiJUFNK1bOp3VVV9SxXgz+hGwnBruBAWdAqhbB6ZHhSA==} + + '@types/pouchdb-adapter-fruitdown@6.1.6': + resolution: {integrity: sha512-KaFB29hUI97eTtJI6pjv7EQcqhZ63qHWovKgyiE+HZF5fVmdrBbTmnIrbR87AJXcXKy47+oQFJ7rzxY8TalpLQ==} + + '@types/pouchdb-adapter-http@6.1.6': + resolution: {integrity: sha512-DJur1mt07GJXwGb5K+MOILoCOSgoQpsi7hybcTzRLeR3IO8Y8eq7TnhTkftAJdx9VHJGOiOXFjO+8BYM69j5yA==} + + '@types/pouchdb-adapter-idb@6.1.7': + resolution: {integrity: sha512-KwjkJ4fTNz5wPXYu20bUoWud7ty0t7tgdo4oc0AJvG+fcURAH7mI7uFmpE4dZIT+hUq5G61xu96AVq9b2q4T3g==} + + '@types/pouchdb-adapter-leveldb@6.1.6': + resolution: {integrity: sha512-mqeTpA2Ni2U4FA5ISRESy4WwhfUahXViUa3jQpXGdSpruaeHlhTLzZJPyz7/mGlvdAfAFv9Vd5d6ys3ASmMujw==} + + '@types/pouchdb-adapter-localstorage@6.1.6': + resolution: {integrity: sha512-+HQBCpD80XkKJE64r7uLwzkNRgkvMnhDI5rIFLx3USxdrRph/R3awcEubRFndcgtxzcUaL9iYw9KetgFMUqPrg==} + + '@types/pouchdb-adapter-memory@6.1.6': + resolution: {integrity: sha512-QCCtW561XuwFACzP/4zYySzs/a4em0EeuQdszen0YOaGV1/fRqJE0dOlmzh8do4sNJomLO6+MFtEzguGljnkgA==} + + '@types/pouchdb-adapter-node-websql@6.1.5': + resolution: {integrity: sha512-yi68syUvHs4OM3mzKlh4zfpov64KITIAnxi387zgdby6SEfAJzWPC0dfH77iEVRDGCrKb3cKTNkl/UGHnphaow==} + + '@types/pouchdb-adapter-websql@6.1.7': + resolution: {integrity: sha512-9oNkP5ZCGMkQALO9KmtbHXlkBq8i2hoCEE6/gWzRicAvL1y+WIKjEQiIIEamMhj5u5tARvW3n2/r+JXwLCyYgw==} + + '@types/pouchdb-browser@6.1.5': + resolution: {integrity: sha512-f+HjxEjYFpgoYWXnMI9AQZZ+SIG8dBiBPrpfWWGsCl+48rumsP5BuBWHq/aXoB8SRKYO0XdP4TNvMBWM3UATCw==} + + '@types/pouchdb-core@7.0.15': + resolution: {integrity: sha512-gq1Qbqn9nCaAKRRv6fRHZ4/ER+QYEwSXBZlDQcxwdbPrtZO8EhIn2Bct0AlguaSEdFcABfbaxxyQwFINkNQ9dQ==} + + '@types/pouchdb-find@7.3.3': + resolution: {integrity: sha512-U7zXk67s9Ar+9Pwj5kSbuMnn8zif0AOOIPy4KRFeJ/S/Tk+mNS90soj+3OV21H8xyB7WTxjvS1JLablZC6C6ow==} + + '@types/pouchdb-http@6.1.5': + resolution: {integrity: sha512-9jGCAl6DUsXIl1vjuPu8tzGykAr84549P4IS0zYdrOKq5eXzQRUb/tb2hEVTmmTcYKXu2P1N55ABsdDNZvzGGA==} + + '@types/pouchdb-mapreduce@6.1.10': + resolution: {integrity: sha512-AgYVqCnaA5D7cWkWyzZVuk0137N4yZsmIQTD/i3DmuMxYYoFrtWUoQu0tbA52SpTRGdL8ubQ7JFQXzA13fA6IQ==} + + '@types/pouchdb-node@6.1.7': + resolution: {integrity: sha512-hryc2eCtNB3GbLcHSwU8glLaY66gDMus1AYkcIYAAxufdnK2BAy1oxaRLmnwRn1A1vG41P/t0htFD161LUnfQw==} + + '@types/pouchdb-replication@6.4.7': + resolution: {integrity: sha512-slB4zOwri3SAVHioFx/FWC/KqOzzb7nDFtV+qzaKzxkf+U5zTwCbK3uRHaj0d/XQk0DwVeajf1ni3Wiyq3j2OA==} + + '@types/pouchdb@6.4.2': + resolution: {integrity: sha512-YsI47rASdtzR+3V3JE2UKY58snhm0AglHBpyckQBkRYoCbTvGagXHtV0x5n8nzN04jQmvTG+Sm85cIzKT3KXBA==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@19.2.2': + resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react-modal@3.16.3': + resolution: {integrity: sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==} + + '@types/react-virtualized@9.22.3': + resolution: {integrity: sha512-UKRWeBIrECaKhE4O//TSFhlgwntMwyiEIOA7WZoVkr52Jahv0dH6YIOorqc358N2V3oKFclsq5XxPmx2PiYB5A==} + + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.180.0': + resolution: {integrity: sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-basic-ssl@2.1.0': + resolution: {integrity: sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + peerDependencies: + vite: ^6.0.0 || ^7.0.0 + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@webgpu/types@0.1.66': + resolution: {integrity: sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==} + + abstract-leveldown@2.7.2: + resolution: {integrity: sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + abstract-leveldown@6.2.3: + resolution: {integrity: sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + abstract-leveldown@6.3.0: + resolution: {integrity: sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + abstract-leveldown@7.2.0: + resolution: {integrity: sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ==} + engines: {node: '>=10'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + astro@5.14.5: + resolution: {integrity: sha512-EHt7y3+nHYyKzBats1AL3N4Pyrvqyr+zXBC7njUa9Tfe+gsiHlunaw+lXitTT/DDVwO2R/f/qVG7Xc6rl0b2KQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.8.16: + resolution: {integrity: sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==} + hasBin: true + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + + browserslist@4.26.3: + resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bun-types@1.3.0: + resolution: {integrity: sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ==} + peerDependencies: + '@types/react': ^19 + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + caniuse-lite@1.0.30001750: + resolution: {integrity: sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==} + + catering@2.1.1: + resolution: {integrity: sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==} + engines: {node: '>=6'} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + deferred-leveldown@5.3.0: + resolution: {integrity: sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + + devalue@5.3.2: + resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + double-ended-queue@2.1.0-0: + resolution: {integrity: sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.237: + resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding-down@6.3.0: + resolution: {integrity: sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + end-stream@0.1.0: + resolution: {integrity: sha512-Brl10T8kYnc75IepKizW6Y9liyW8ikz1B7n/xoHrJxoVSSjoqPn30sb7XVFfQERK4QfUMYRGs9dhWwtt2eu6uA==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-toolkit@1.40.0: + resolution: {integrity: sha512-8o6w0KFmU0CiIl0/Q/BCEOabF2IJaELM1T2PWj6e8KqzHv1gdx+7JtFnDwOx1kJH/isJ5NwlDG1nCr1HrRF94Q==} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exenv@1.2.2: + resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-cookie@2.2.0: + resolution: {integrity: sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + flatbuffers@25.9.23: + resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.3.1: + resolution: {integrity: sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg==} + + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphology-types@0.24.8: + resolution: {integrity: sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==} + + graphology-utils@2.5.2: + resolution: {integrity: sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==} + peerDependencies: + graphology-types: '>=0.23.0' + + graphology@0.26.0: + resolution: {integrity: sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg==} + peerDependencies: + graphology-types: '>=0.24.0' + + guid-typescript@1.0.9: + resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==} + + h3@1.15.4: + resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-parse-stringify@3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + i18next-browser-languagedetector@8.2.0: + resolution: {integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==} + + i18next-http-backend@3.0.2: + resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==} + + i18next@25.6.0: + resolution: {integrity: sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + immediate@3.3.0: + resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + level-codec@9.0.2: + resolution: {integrity: sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==} + engines: {node: '>=6'} + deprecated: Superseded by level-transcoder (https://github.com/Level/community#faq) + + level-concat-iterator@2.0.1: + resolution: {integrity: sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-concat-iterator@3.1.0: + resolution: {integrity: sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ==} + engines: {node: '>=10'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-errors@2.0.1: + resolution: {integrity: sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-iterator-stream@4.0.2: + resolution: {integrity: sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==} + engines: {node: '>=6'} + + level-js@5.0.2: + resolution: {integrity: sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==} + deprecated: Superseded by browser-level (https://github.com/Level/community#faq) + + level-packager@5.1.1: + resolution: {integrity: sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-supports@1.0.1: + resolution: {integrity: sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==} + engines: {node: '>=6'} + + level-supports@2.1.0: + resolution: {integrity: sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA==} + engines: {node: '>=10'} + + level-write-stream@1.0.0: + resolution: {integrity: sha512-bBNKOEOMl8msO+uIM9YX/gUO6ckokZ/4pCwTm/lwvs46x6Xs8Zy0sn3Vh37eDqse4mhy4fOMIb/JsSM2nyQFtw==} + + level@6.0.1: + resolution: {integrity: sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==} + engines: {node: '>=8.6.0'} + + leveldown@5.6.0: + resolution: {integrity: sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==} + engines: {node: '>=8.6.0'} + deprecated: Superseded by classic-level (https://github.com/Level/community#faq) + + leveldown@6.1.1: + resolution: {integrity: sha512-88c+E+Eizn4CkQOBHwqlCJaTNEjGpaEIikn1S+cINc5E9HEvJ77bqY4JY/HxT5u0caWqsc3P3DcFIKBI1vHt+A==} + engines: {node: '>=10.12.0'} + deprecated: Superseded by classic-level (https://github.com/Level/community#faq) + + levelup@4.4.0: + resolution: {integrity: sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + ltgt@2.2.1: + resolution: {integrity: sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==} + + lucide-react@0.509.0: + resolution: {integrity: sha512-xCJHn6Uh5qF6PGml25vveCTrHJZcqS1G1MVzWZK54ZQsOiCVJk4fwY3oyo5EXS2S+aqvTpWYIfJN+PesJ0quxg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide-react@0.545.0: + resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + marked@16.4.1: + resolution: {integrity: sha512-ntROs7RaN3EvWfy3EZi14H4YxmT6A5YvywfhO+0pm+cH/dnSQRmdAmoFIc3B9aiwTehyk7pESH4ofyBY+V5hZg==} + engines: {node: '>= 20'} + hasBin: true + + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + memdown@1.4.1: + resolution: {integrity: sha512-iVrGHZB8i4OQfM155xx8akvG9FIj+ht14DX5CQkCTG4EHzZ3d3sgckIf/Lm9ivZalEsFuEVnWv2B2WZvbrro2w==} + deprecated: Superseded by memory-level (https://github.com/Level/community#faq) + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + meshoptimizer@0.22.0: + resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + engines: {node: ^18 || >=20} + hasBin: true + + napi-macros@2.0.0: + resolution: {integrity: sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==} + + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.6.9: + resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-gyp-build@4.1.1: + resolution: {integrity: sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==} + hasBin: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-mock-http@1.0.3: + resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==} + + node-releases@2.0.23: + resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + + onnxruntime-common@1.23.0: + resolution: {integrity: sha512-Auz8S9D7vpF8ok7fzTobvD1XdQDftRf/S7pHmjeCr3Xdymi4z1C7zx4vnT6nnUjbpelZdGwda0BmWHCCTMKUTg==} + + onnxruntime-web@1.23.0: + resolution: {integrity: sha512-w0bvC2RwDxphOUFF8jFGZ/dYw+duaX20jM6V4BIZJPCfK4QuCpB/pVREV+hjYbT3x4hyfa2ZbTaWx4e1Vot0fQ==} + + openai@5.23.2: + resolution: {integrity: sha512-MQBzmTulj+MM5O8SKEk/gL8a7s5mktS9zUtAkU257WjvobGc9nKcBuVwjyEEcb9SI8a8Y2G/mzn3vm9n1Jlleg==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + p-limit@6.2.0: + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} + + p-queue@8.1.1: + resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} + engines: {node: '>=18'} + + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + + package-manager-detector@1.4.1: + resolution: {integrity: sha512-dSMiVLBEA4XaNJ0PRb4N5cV/SEP4BWrWZKBmfF+OUm2pQTiZ6DDkKeWaltwu3JRhLoy59ayIkJ00cx9K9CaYTg==} + + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + platform@1.3.6: + resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + + pocketbase@0.26.2: + resolution: {integrity: sha512-WA8EOBc3QnSJh8rJ3iYoi9DmmPOMFIgVfAmIGux7wwruUEIzXgvrO4u0W2htfQjGIcyezJkdZOy5Xmh7SxAftw==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + pouchdb-adapter-leveldb-core@9.0.0: + resolution: {integrity: sha512-b3ZGPtVXyivGL5SK3AIDG7PrNsZdoDpGFkmTytDTtctkVhxOg71gnXXP+CrupENPqSNG/eGbKW4w+bbMpxy6aA==} + + pouchdb-adapter-memory@9.0.0: + resolution: {integrity: sha512-XbCwJ5f5U9dGdkiDikzYjTebdPHuA6Ghylx1Pq0lDe4y6l8R9xhjDSUy56pJ8G2F4Z+8QdB5FBY9EQoFlFSXWQ==} + + pouchdb-adapter-utils@9.0.0: + resolution: {integrity: sha512-hmbm4ey0HL0vtoY1tRTPIt2FfYjvMh3DWoGGSxXDTS73qTFQ+Fhhi5I0AnN9PcD2omfKQAVXiYks4kkMvlAHqA==} + + pouchdb-binary-utils@9.0.0: + resolution: {integrity: sha512-2OMtgDZi82vqs+zNDE0YiYjOaWkYCUcZJZKK3WkRr+XYRu+2B7umJrnygJFhUwoGedBbHSrlQBLhdNV3F1AX1A==} + + pouchdb-browser@9.0.0: + resolution: {integrity: sha512-0uKFWhsTtiVOF0+aGo7mvtCTP40f6dlsLNmJUvc/lwjsX1C3v+eBfVbvykyxpFl7UTAoJkXl+g/GOzNvyMtV1g==} + + pouchdb-changes-filter@9.0.0: + resolution: {integrity: sha512-ig0fo0WLgIjAniFJ19Uw1Y+oxiypqC+Skhd8BCETRVXOhLBzueRwEQR4thffyo0UayYVqldJfSR5wHSDvEVk/A==} + + pouchdb-collate@9.0.0: + resolution: {integrity: sha512-TrnEDNZEmIIl+W3xKUO8h+geqVLQ90oZe5ujPkl8myUzpREULWXWQBnV5EzPXVEKDBpJlb8T3I6oy/zdWGQpdA==} + + pouchdb-core@9.0.0: + resolution: {integrity: sha512-98SJgs8bqXhr4gMGuOTR8yVeLlMYy797zlOtdlvlXIxIicvocyA8ColhVVhdBXPNOGxT2HwReIMywdIVAgibpg==} + + pouchdb-errors@9.0.0: + resolution: {integrity: sha512-961PSMLhW0UqqdJ566g+CdLZ5pkBJRd6l4WWpCDdD0USvE4xYfYGzv43w7nZZBw1k3Xdy092yqPge7yX/tfnyw==} + + pouchdb-fetch@9.0.0: + resolution: {integrity: sha512-TbE3cUcAJQrwb9kr44tDP0X+NAbcqgjsTvcL30L4xzBNJeCPTIRjukYX80s154SHJUXBxcWRiPsMmNqpXsjfCA==} + + pouchdb-json@9.0.0: + resolution: {integrity: sha512-aI41mYVyI195GXuT1Ys7mLIB/Mvrz11ihoTP6km6hYqVgSuaUxuZcFUozlyTJiZXr7H5kdhNgclhlVnjir4JAA==} + + pouchdb-md5@9.0.0: + resolution: {integrity: sha512-58xUYBvW3/s+aH0j4uOhhN8yCk0LQ254cxBzI/gbKA9PrfwHpe4zrr0L/ia5ml3A30oH1f8aTnuVMwWDkFcuww==} + + pouchdb-merge@9.0.0: + resolution: {integrity: sha512-Xh+TgOZCkGoZpI589btKf/cTiuQ5CsnPl9YpdW4h0cAPusniN6XNsR62F+/HbL9wirI6XTEPHUrk7MsQbk3S3A==} + + pouchdb-selector-core@9.0.0: + resolution: {integrity: sha512-ZYHYsdoedwm8j5tYofz+3+uUSK8i+7tRCBb01T0OuqDQb17+w5mzjHF8Ppi160xdPUPaWCo1Un+nLWGJzkmA3g==} + + pouchdb-utils@9.0.0: + resolution: {integrity: sha512-xWZE5c+nAslgmLC8JBZbky8AYgdz7pKtv7KTSi6CD2tuQD0WyNKib0YnhZndeE84dksTeZlqlg56RQHsHoB2LQ==} + + pouchdb@9.0.0: + resolution: {integrity: sha512-6wjFc/PzwaWz86rmMXoqdBlR/fBSkNoWO1mEJO7RZNS6n3xf+fhhXWAWtws741KpLKx84IkmmJ48tp+fhFzj4A==} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + peerDependencies: + react: ^19.2.0 + + react-i18next@15.7.4: + resolution: {integrity: sha512-nyU8iKNrI5uDJch0z9+Y5XEr34b0wkyYj3Rp+tfbahxtlswxSCjcUL9H0nqXo9IR3/t5Y5PKIA3fx3MfUyR9Xw==} + peerDependencies: + i18next: '>= 23.4.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + typescript: ^5 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + + react-modal@3.16.3: + resolution: {integrity: sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw==} + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 + react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-resizable-panels@3.0.6: + resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==} + peerDependencies: + react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + react-toastify@11.0.5: + resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==} + peerDependencies: + react: ^18 || ^19 + react-dom: ^18 || ^19 + + react-transition-state@2.3.1: + resolution: {integrity: sha512-Z48el73x+7HUEM131dof9YpcQ5IlM4xB+pKWH/lX3FhxGfQaNTZa16zb7pWkC/y5btTZzXfCtglIJEGc57giOw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-virtualized@9.22.6: + resolution: {integrity: sha512-U5j7KuUQt3AaMatlMJ0UJddqSiX+Km0YJxSqbAzIiGw5EmNz0khMyqP2hzgu4+QUtm+QPIrxzUX4raJxmVJnHg==} + peerDependencies: + react: ^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + engines: {node: '>=0.10.0'} + + readable-stream@0.0.4: + resolution: {integrity: sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw==} + + readable-stream@1.1.14: + resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup-plugin-dts@6.2.3: + resolution: {integrity: sha512-UgnEsfciXSPpASuOelix7m4DrmyQgiaWBnvI0TM4GxuDh5FkqW8E5hu57bCxXB90VvR1WNfLV80yEDN18UogSA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + + rollup@4.52.4: + resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + selfsigned@3.0.1: + resolution: {integrity: sha512-6U6w6kSLrM9Zxo0D7mC7QdGS6ZZytMWBnj/vhF9p+dAHx6CwGezuRcO4VclTbrrI7mg7SD6zNiqXUuBHOVopNQ==} + engines: {node: '>=10'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shiki@3.13.0: + resolution: {integrity: sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==} + + sigma@3.0.2: + resolution: {integrity: sha512-/BUbeOwPGruiBOm0YQQ6ZMcLIZ6tf/W+Jcm7dxZyAX0tK3WP9/sq7/NAWBxPIxVahdGjCJoGwej0Gdrv0DxlQQ==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@8.0.0: + resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + + smol-toml@1.4.2: + resolution: {integrity: sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + spark-md5@3.0.2: + resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + style-to-js@1.1.18: + resolution: {integrity: sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==} + + style-to-object@1.0.11: + resolution: {integrity: sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow==} + + sublevel-pouchdb@9.0.0: + resolution: {integrity: sha512-pX4r8+F7wuts0C81kUJ341h4bl2aRe7qV572FE8X1FMz9VkKlmi2nPD1vfeiOJXz5Y09I4MHjGULAbqvTfQZEQ==} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwindcss@4.1.14: + resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tar@7.5.1: + resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} + engines: {node: '>=18'} + + three@0.180.0: + resolution: {integrity: sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==} + + through2@3.0.2: + resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + turbo-darwin-64@2.5.8: + resolution: {integrity: sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.5.8: + resolution: {integrity: sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.5.8: + resolution: {integrity: sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.5.8: + resolution: {integrity: sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.5.8: + resolution: {integrity: sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.5.8: + resolution: {integrity: sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ==} + cpu: [arm64] + os: [win32] + + turbo@2.5.8: + resolution: {integrity: sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w==} + hasBin: true + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} + + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.6.0: + resolution: {integrity: sha512-5Fx50fFQMQL5aeHyWnZX9122sSLckcDvcfFiBf3QYeHa7a1MKJooUy52b67moi2MJYkrfo/TWY+CoLdr/w0tTA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + unstorage@1.17.1: + resolution: {integrity: sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@6.3.7: + resolution: {integrity: sha512-mQYaKepA0NGMBsz8Xktt3tJUG5ELE2iT7IJ+ssXI6nxVdE2sFc/d/6w/JByqMLvWg8hNKHpPgzjgOkrhpKFnrA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + + vuvuzela@1.0.3: + resolution: {integrity: sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ==} + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + wavesurfer.js@7.11.0: + resolution: {integrity: sha512-LOGdIBIKv/roYuQYClhoqhwbIdQL1GfobLnS2vx0heoLD9lu57OUHWE2DIsCNXBvCsmmbkUvJq9W8bPLPbikGw==} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + write-stream@0.4.3: + resolution: {integrity: sha512-IJrvkhbAnj89W/GAVdVgbnPiVw5Ntg/B4tc/MUCIEwj/g6JIww1DWJyB/yBMT3yw2/TkT6IUZ0+IYef3flEw8A==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yocto-spinner@0.2.3: + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zustand@5.0.8: + resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@astrojs/compiler@2.13.0': {} + + '@astrojs/internal-helpers@0.7.4': {} + + '@astrojs/markdown-remark@6.3.8': + dependencies: + '@astrojs/internal-helpers': 0.7.4 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.13.0 + smol-toml: 1.4.2 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/mdx@4.3.7(astro@5.14.5(@types/node@24.7.2)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.52.4)(typescript@5.9.3))': + dependencies: + '@astrojs/markdown-remark': 6.3.8 + '@mdx-js/mdx': 3.1.1 + acorn: 8.15.0 + astro: 5.14.5(@types/node@24.7.2)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.52.4)(typescript@5.9.3) + es-module-lexer: 1.7.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.5 + kleur: 4.1.5 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-smartypants: 3.0.2 + source-map: 0.7.6 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.3.0': + dependencies: + prismjs: 1.30.0 + + '@astrojs/react@4.4.0(@types/node@24.7.2)(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@vitejs/plugin-react': 4.7.0(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + ultrahtml: 1.6.0 + vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@astrojs/sitemap@3.6.0': + dependencies: + sitemap: 8.0.0 + stream-replace-string: 2.0.0 + zod: 3.25.76 + + '@astrojs/telemetry@3.3.0': + dependencies: + ci-info: 4.3.1 + debug: 4.4.3 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.4': {} + + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.26.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@capsizecss/unpack@3.0.0': + dependencies: + fontkit: 2.0.4 + + '@dimforge/rapier3d-compat@0.12.0': {} + + '@emnapi/runtime@1.5.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.11': + optional: true + + '@esbuild/android-arm64@0.25.11': + optional: true + + '@esbuild/android-arm@0.25.11': + optional: true + + '@esbuild/android-x64@0.25.11': + optional: true + + '@esbuild/darwin-arm64@0.25.11': + optional: true + + '@esbuild/darwin-x64@0.25.11': + optional: true + + '@esbuild/freebsd-arm64@0.25.11': + optional: true + + '@esbuild/freebsd-x64@0.25.11': + optional: true + + '@esbuild/linux-arm64@0.25.11': + optional: true + + '@esbuild/linux-arm@0.25.11': + optional: true + + '@esbuild/linux-ia32@0.25.11': + optional: true + + '@esbuild/linux-loong64@0.25.11': + optional: true + + '@esbuild/linux-mips64el@0.25.11': + optional: true + + '@esbuild/linux-ppc64@0.25.11': + optional: true + + '@esbuild/linux-riscv64@0.25.11': + optional: true + + '@esbuild/linux-s390x@0.25.11': + optional: true + + '@esbuild/linux-x64@0.25.11': + optional: true + + '@esbuild/netbsd-arm64@0.25.11': + optional: true + + '@esbuild/netbsd-x64@0.25.11': + optional: true + + '@esbuild/openbsd-arm64@0.25.11': + optional: true + + '@esbuild/openbsd-x64@0.25.11': + optional: true + + '@esbuild/openharmony-arm64@0.25.11': + optional: true + + '@esbuild/sunos-x64@0.25.11': + optional: true + + '@esbuild/win32-arm64@0.25.11': + optional: true + + '@esbuild/win32-ia32@0.25.11': + optional: true + + '@esbuild/win32-x64@0.25.11': + optional: true + + '@faker-js/faker@10.1.0': {} + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + '@floating-ui/react@0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@floating-ui/utils': 0.2.10 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + tabbable: 6.2.0 + + '@floating-ui/utils@0.2.10': {} + + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.3 + optional: true + + '@img/sharp-darwin-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + optional: true + + '@img/sharp-linux-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.3 + optional: true + + '@img/sharp-linux-arm@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.3 + optional: true + + '@img/sharp-linux-ppc64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.3 + optional: true + + '@img/sharp-linux-s390x@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.3 + optional: true + + '@img/sharp-linux-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + optional: true + + '@img/sharp-wasm32@0.34.4': + dependencies: + '@emnapi/runtime': 1.5.0 + optional: true + + '@img/sharp-win32-arm64@0.34.4': + optional: true + + '@img/sharp-win32-ia32@0.34.4': + optional: true + + '@img/sharp-win32-x64@0.34.4': + optional: true + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@kevisual/cache@0.0.2(rollup@4.52.4)(tslib@2.8.1)(typescript@5.9.3)': + dependencies: + '@rollup/plugin-commonjs': 28.0.7(rollup@4.52.4) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.52.4) + '@rollup/plugin-typescript': 12.1.4(rollup@4.52.4)(tslib@2.8.1)(typescript@5.9.3) + idb-keyval: 6.2.2 + rollup-plugin-dts: 6.2.3(rollup@4.52.4)(typescript@5.9.3) + transitivePeerDependencies: + - rollup + - tslib + - typescript + + '@kevisual/load@0.0.6': + dependencies: + eventemitter3: 5.0.1 + + '@kevisual/local-proxy@0.0.6': {} + + '@kevisual/noco@0.0.1': {} + + '@kevisual/query-login@0.0.6(@kevisual/query@0.0.29(zod@3.25.76))(rollup@4.52.4)(tslib@2.8.1)(typescript@5.9.3)': + dependencies: + '@kevisual/cache': 0.0.2(rollup@4.52.4)(tslib@2.8.1)(typescript@5.9.3) + '@kevisual/query': 0.0.29(zod@3.25.76) + dotenv: 16.6.1 + transitivePeerDependencies: + - rollup + - tslib + - typescript + + '@kevisual/query@0.0.29(zod@3.25.76)': + dependencies: + openai: 5.23.2(zod@3.25.76) + transitivePeerDependencies: + - ws + - zod + + '@kevisual/registry@0.0.1(typescript@5.9.3)': + dependencies: + class-variance-authority: 0.7.1 + clsx: 2.1.1 + i18next: 25.6.0(typescript@5.9.3) + i18next-browser-languagedetector: 8.2.0 + i18next-http-backend: 3.0.2 + lucide-react: 0.509.0(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-i18next: 15.7.4(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react-toastify: 11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + tailwind-merge: 3.3.1 + transitivePeerDependencies: + - encoding + - react-native + - typescript + + '@kevisual/router@0.0.29': + dependencies: + path-to-regexp: 8.3.0 + selfsigned: 3.0.1 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + '@kevisual/types@0.0.10': {} + + '@kevisual/use-config@1.0.19(dotenv@17.2.3)': + dependencies: + '@kevisual/load': 0.0.6 + dotenv: 17.2.3 + + '@mdx-js/mdx@3.1.1': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + acorn: 8.15.0 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.1(acorn@8.15.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.6 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@oslojs/encoding@1.1.0': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@ricky0123/vad-web@0.0.28': + dependencies: + onnxruntime-web: 1.23.0 + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/plugin-commonjs@28.0.7(rollup@4.52.4)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.52.4) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.19 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.52.4 + + '@rollup/plugin-node-resolve@16.0.3(rollup@4.52.4)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.52.4) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 + optionalDependencies: + rollup: 4.52.4 + + '@rollup/plugin-typescript@12.1.4(rollup@4.52.4)(tslib@2.8.1)(typescript@5.9.3)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.52.4) + resolve: 1.22.10 + typescript: 5.9.3 + optionalDependencies: + rollup: 4.52.4 + tslib: 2.8.1 + + '@rollup/pluginutils@5.3.0(rollup@4.52.4)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.52.4 + + '@rollup/rollup-android-arm-eabi@4.52.4': + optional: true + + '@rollup/rollup-android-arm64@4.52.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.4': + optional: true + + '@rollup/rollup-darwin-x64@4.52.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.52.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.4': + optional: true + + '@shikijs/core@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + + '@shikijs/themes@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + + '@shikijs/types@3.13.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@szhsin/react-menu@4.5.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-transition-state: 2.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + + '@tailwindcss/node@4.1.14': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.14 + + '@tailwindcss/oxide-android-arm64@4.1.14': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.14': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.14': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.14': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.14': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.14': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': + optional: true + + '@tailwindcss/oxide@4.1.14': + dependencies: + detect-libc: 2.1.2 + tar: 7.5.1 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-x64': 4.1.14 + '@tailwindcss/oxide-freebsd-x64': 4.1.14 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.14 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-x64-musl': 4.1.14 + '@tailwindcss/oxide-wasm32-wasi': 4.1.14 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.14 + + '@tailwindcss/vite@4.1.14(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + '@tailwindcss/node': 4.1.14 + '@tailwindcss/oxide': 4.1.14 + tailwindcss: 4.1.14 + vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1) + + '@tweenjs/tween.js@23.1.3': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/bun@1.3.0(@types/react@19.2.2)': + dependencies: + bun-types: 1.3.0(@types/react@19.2.2) + transitivePeerDependencies: + - '@types/react' + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/fontkit@2.0.8': + dependencies: + '@types/node': 24.7.2 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/ms@2.1.0': {} + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@17.0.45': {} + + '@types/node@24.7.2': + dependencies: + undici-types: 7.14.0 + + '@types/pouchdb-adapter-cordova-sqlite@1.0.4': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-fruitdown@6.1.6': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-http@6.1.6': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-idb@6.1.7': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-leveldb@6.1.6': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-localstorage@6.1.6': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-memory@6.1.6': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-node-websql@6.1.5': + dependencies: + '@types/pouchdb-adapter-websql': 6.1.7 + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-adapter-websql@6.1.7': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-browser@6.1.5': + dependencies: + '@types/pouchdb-adapter-http': 6.1.6 + '@types/pouchdb-adapter-idb': 6.1.7 + '@types/pouchdb-adapter-websql': 6.1.7 + '@types/pouchdb-core': 7.0.15 + '@types/pouchdb-mapreduce': 6.1.10 + '@types/pouchdb-replication': 6.4.7 + + '@types/pouchdb-core@7.0.15': + dependencies: + '@types/debug': 4.1.12 + '@types/pouchdb-find': 7.3.3 + + '@types/pouchdb-find@7.3.3': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-http@6.1.5': + dependencies: + '@types/pouchdb-adapter-http': 6.1.6 + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-mapreduce@6.1.10': + dependencies: + '@types/pouchdb-core': 7.0.15 + + '@types/pouchdb-node@6.1.7': + dependencies: + '@types/pouchdb-adapter-http': 6.1.6 + '@types/pouchdb-adapter-leveldb': 6.1.6 + '@types/pouchdb-core': 7.0.15 + '@types/pouchdb-mapreduce': 6.1.10 + '@types/pouchdb-replication': 6.4.7 + + '@types/pouchdb-replication@6.4.7': + dependencies: + '@types/pouchdb-core': 7.0.15 + '@types/pouchdb-find': 7.3.3 + + '@types/pouchdb@6.4.2': + dependencies: + '@types/pouchdb-adapter-cordova-sqlite': 1.0.4 + '@types/pouchdb-adapter-fruitdown': 6.1.6 + '@types/pouchdb-adapter-http': 6.1.6 + '@types/pouchdb-adapter-idb': 6.1.7 + '@types/pouchdb-adapter-leveldb': 6.1.6 + '@types/pouchdb-adapter-localstorage': 6.1.6 + '@types/pouchdb-adapter-memory': 6.1.6 + '@types/pouchdb-adapter-node-websql': 6.1.5 + '@types/pouchdb-adapter-websql': 6.1.7 + '@types/pouchdb-browser': 6.1.5 + '@types/pouchdb-core': 7.0.15 + '@types/pouchdb-http': 6.1.5 + '@types/pouchdb-mapreduce': 6.1.10 + '@types/pouchdb-node': 6.1.7 + '@types/pouchdb-replication': 6.4.7 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@19.2.2(@types/react@19.2.2)': + dependencies: + '@types/react': 19.2.2 + + '@types/react-modal@3.16.3': + dependencies: + '@types/react': 19.2.2 + + '@types/react-virtualized@9.22.3': + dependencies: + '@types/prop-types': 15.7.15 + '@types/react': 19.2.2 + + '@types/react@19.2.2': + dependencies: + csstype: 3.1.3 + + '@types/resolve@1.20.2': {} + + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.7.2 + + '@types/stats.js@0.17.4': {} + + '@types/three@0.180.0': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + '@webgpu/types': 0.1.66 + fflate: 0.8.2 + meshoptimizer: 0.22.0 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/webxr@0.5.24': {} + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-basic-ssl@2.1.0(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1) + + '@vitejs/plugin-react@4.7.0(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1) + transitivePeerDependencies: + - supports-color + + '@webgpu/types@0.1.66': {} + + abstract-leveldown@2.7.2: + dependencies: + xtend: 4.0.2 + + abstract-leveldown@6.2.3: + dependencies: + buffer: 5.7.1 + immediate: 3.3.0 + level-concat-iterator: 2.0.1 + level-supports: 1.0.1 + xtend: 4.0.2 + + abstract-leveldown@6.3.0: + dependencies: + buffer: 5.7.1 + immediate: 3.3.0 + level-concat-iterator: 2.0.1 + level-supports: 1.0.1 + xtend: 4.0.2 + + abstract-leveldown@7.2.0: + dependencies: + buffer: 6.0.3 + catering: 2.1.1 + is-buffer: 2.0.5 + level-concat-iterator: 3.1.0 + level-supports: 2.1.0 + queue-microtask: 1.2.3 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@6.2.3: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} + + astring@1.9.0: {} + + astro@5.14.5(@types/node@24.7.2)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.52.4)(typescript@5.9.3): + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/internal-helpers': 0.7.4 + '@astrojs/markdown-remark': 6.3.8 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 3.0.0 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.52.4) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.3.1 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.0.2 + cssesc: 3.0.0 + debug: 4.4.3 + deterministic-object-hash: 2.0.2 + devalue: 5.3.2 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.11 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.19 + magicast: 0.3.5 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.4.1 + picomatch: 4.0.3 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.3 + shiki: 3.13.0 + smol-toml: 1.4.2 + tinyexec: 1.0.1 + tinyglobby: 0.2.15 + tsconfck: 3.1.6(typescript@5.9.3) + ultrahtml: 1.6.0 + unifont: 0.6.0 + unist-util-visit: 5.0.0 + unstorage: 1.17.1(idb-keyval@6.2.2) + vfile: 6.0.3 + vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1) + vitefu: 1.1.1(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.4 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + base-64@1.0.0: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.8.16: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + + browserslist@4.26.3: + dependencies: + baseline-browser-mapping: 2.8.16 + caniuse-lite: 1.0.30001750 + electron-to-chromium: 1.5.237 + node-releases: 2.0.23 + update-browserslist-db: 1.1.3(browserslist@4.26.3) + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bun-types@1.3.0(@types/react@19.2.2): + dependencies: + '@types/node': 24.7.2 + '@types/react': 19.2.2 + + camelcase@8.0.0: {} + + caniuse-lite@1.0.30001750: {} + + catering@2.1.1: {} + + ccount@2.0.1: {} + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@3.0.0: {} + + ci-info@4.3.1: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + cli-boxes@3.0.0: {} + + clone@2.1.2: {} + + clsx@1.2.1: {} + + clsx@2.1.1: {} + + collapse-white-space@2.1.0: {} + + comma-separated-tokens@2.0.3: {} + + common-ancestor-path@1.0.1: {} + + commondir@1.0.1: {} + + convert-source-map@2.0.0: {} + + cookie-es@1.2.2: {} + + cookie@1.0.2: {} + + core-util-is@1.0.3: {} + + cross-fetch@4.0.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + + csstype@3.1.3: {} + + dayjs@1.11.18: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + deepmerge@4.3.1: {} + + deferred-leveldown@5.3.0: + dependencies: + abstract-leveldown: 6.2.3 + inherits: 2.0.4 + + defu@6.1.4: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-libc@2.1.2: {} + + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.3.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dfa@1.2.0: {} + + diff@5.2.0: {} + + dlv@1.1.3: {} + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.4 + csstype: 3.1.3 + + dotenv@16.6.1: {} + + dotenv@17.2.3: {} + + double-ended-queue@2.1.0-0: {} + + dset@3.1.4: {} + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.237: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + encodeurl@2.0.0: {} + + encoding-down@6.3.0: + dependencies: + abstract-leveldown: 6.3.0 + inherits: 2.0.4 + level-codec: 9.0.2 + level-errors: 2.0.1 + + end-stream@0.1.0: + dependencies: + write-stream: 0.4.3 + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + entities@6.0.1: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + + es-module-lexer@1.7.0: {} + + es-toolkit@1.40.0: {} + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.15.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.3 + + esbuild@0.25.11: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.6 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + etag@1.8.1: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + exenv@1.2.2: {} + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fetch-cookie@2.2.0: + dependencies: + set-cookie-parser: 2.7.1 + tough-cookie: 4.1.4 + + fflate@0.8.2: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + flatbuffers@25.9.23: {} + + flattie@1.1.1: {} + + fontace@0.3.1: + dependencies: + '@types/fontkit': 2.0.8 + fontkit: 2.0.4 + + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.17 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + + fresh@2.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + functional-red-black-tree@1.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-east-asian-width@1.4.0: {} + + github-slugger@2.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + graceful-fs@4.2.11: {} + + graphology-types@0.24.8: {} + + graphology-utils@2.5.2(graphology-types@0.24.8): + dependencies: + graphology-types: 0.24.8 + + graphology@0.26.0(graphology-types@0.24.8): + dependencies: + events: 3.3.0 + graphology-types: 0.24.8 + + guid-typescript@1.0.9: {} + + h3@1.15.4: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.3 + radix3: 1.1.2 + ufo: 1.6.1 + uncrypto: 0.1.3 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.18 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.18 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + highlight.js@11.11.1: {} + + html-escaper@3.0.3: {} + + html-parse-stringify@3.0.1: + dependencies: + void-elements: 3.1.0 + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.2.0: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + i18next-browser-languagedetector@8.2.0: + dependencies: + '@babel/runtime': 7.28.4 + + i18next-http-backend@3.0.2: + dependencies: + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + + i18next@25.6.0(typescript@5.9.3): + dependencies: + '@babel/runtime': 7.28.4 + optionalDependencies: + typescript: 5.9.3 + + idb-keyval@6.2.2: {} + + ieee754@1.2.1: {} + + immediate@3.3.0: {} + + import-meta-resolve@4.2.0: {} + + inherits@2.0.4: {} + + inline-style-parser@0.2.4: {} + + iron-webcrypto@1.2.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-buffer@2.0.5: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-decimal@2.0.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-module@1.0.0: {} + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.8 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isarray@0.0.1: {} + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + level-codec@9.0.2: + dependencies: + buffer: 5.7.1 + + level-concat-iterator@2.0.1: {} + + level-concat-iterator@3.1.0: + dependencies: + catering: 2.1.1 + + level-errors@2.0.1: + dependencies: + errno: 0.1.8 + + level-iterator-stream@4.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + + level-js@5.0.2: + dependencies: + abstract-leveldown: 6.2.3 + buffer: 5.7.1 + inherits: 2.0.4 + ltgt: 2.2.1 + + level-packager@5.1.1: + dependencies: + encoding-down: 6.3.0 + levelup: 4.4.0 + + level-supports@1.0.1: + dependencies: + xtend: 4.0.2 + + level-supports@2.1.0: {} + + level-write-stream@1.0.0: + dependencies: + end-stream: 0.1.0 + + level@6.0.1: + dependencies: + level-js: 5.0.2 + level-packager: 5.1.1 + leveldown: 5.6.0 + + leveldown@5.6.0: + dependencies: + abstract-leveldown: 6.2.3 + napi-macros: 2.0.0 + node-gyp-build: 4.1.1 + + leveldown@6.1.1: + dependencies: + abstract-leveldown: 7.2.0 + napi-macros: 2.0.0 + node-gyp-build: 4.8.4 + + levelup@4.4.0: + dependencies: + deferred-leveldown: 5.3.0 + level-errors: 2.0.1 + level-iterator-stream: 4.0.2 + level-supports: 1.0.1 + xtend: 4.0.2 + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + lodash-es@4.17.21: {} + + long@5.3.2: {} + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + ltgt@2.2.1: {} + + lucide-react@0.509.0(react@19.2.0): + dependencies: + react: 19.2.0 + + lucide-react@0.545.0(react@19.2.0): + dependencies: + react: 19.2.0 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + source-map-js: 1.2.1 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + marked@16.4.1: {} + + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.12.2: {} + + memdown@1.4.1: + dependencies: + abstract-leveldown: 2.7.2 + functional-red-black-tree: 1.0.1 + immediate: 3.3.0 + inherits: 2.0.4 + ltgt: 2.2.1 + safe-buffer: 5.1.2 + + merge2@1.4.1: {} + + meshoptimizer@0.22.0: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.8 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + minipass@7.1.2: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + nanoid@5.1.6: {} + + napi-macros@2.0.0: {} + + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + + node-fetch-native@1.6.7: {} + + node-fetch@2.6.9: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-forge@1.3.1: {} + + node-gyp-build@4.1.1: {} + + node-gyp-build@4.8.4: {} + + node-mock-http@1.0.3: {} + + node-releases@2.0.23: {} + + normalize-path@3.0.0: {} + + object-assign@4.1.1: {} + + ofetch@1.4.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.1 + + ohash@2.0.11: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + + onnxruntime-common@1.23.0: {} + + onnxruntime-web@1.23.0: + dependencies: + flatbuffers: 25.9.23 + guid-typescript: 1.0.9 + long: 5.3.2 + onnxruntime-common: 1.23.0 + platform: 1.3.6 + protobufjs: 7.5.4 + + openai@5.23.2(zod@3.25.76): + optionalDependencies: + zod: 3.25.76 + + p-limit@6.2.0: + dependencies: + yocto-queue: 1.2.1 + + p-queue@8.1.1: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.4 + + p-timeout@6.1.4: {} + + package-manager-detector@1.4.1: {} + + pako@0.2.9: {} + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-parse@1.0.7: {} + + path-to-regexp@8.3.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + platform@1.3.6: {} + + pocketbase@0.26.2: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pouchdb-adapter-leveldb-core@9.0.0: + dependencies: + double-ended-queue: 2.1.0-0 + levelup: 4.4.0 + pouchdb-adapter-utils: 9.0.0 + pouchdb-binary-utils: 9.0.0 + pouchdb-core: 9.0.0 + pouchdb-errors: 9.0.0 + pouchdb-json: 9.0.0 + pouchdb-md5: 9.0.0 + pouchdb-merge: 9.0.0 + pouchdb-utils: 9.0.0 + sublevel-pouchdb: 9.0.0 + through2: 3.0.2 + transitivePeerDependencies: + - encoding + + pouchdb-adapter-memory@9.0.0: + dependencies: + memdown: 1.4.1 + pouchdb-adapter-leveldb-core: 9.0.0 + transitivePeerDependencies: + - encoding + + pouchdb-adapter-utils@9.0.0: + dependencies: + pouchdb-binary-utils: 9.0.0 + pouchdb-errors: 9.0.0 + pouchdb-md5: 9.0.0 + pouchdb-merge: 9.0.0 + pouchdb-utils: 9.0.0 + + pouchdb-binary-utils@9.0.0: {} + + pouchdb-browser@9.0.0: + dependencies: + spark-md5: 3.0.2 + uuid: 8.3.2 + vuvuzela: 1.0.3 + + pouchdb-changes-filter@9.0.0: + dependencies: + pouchdb-errors: 9.0.0 + pouchdb-selector-core: 9.0.0 + pouchdb-utils: 9.0.0 + + pouchdb-collate@9.0.0: {} + + pouchdb-core@9.0.0: + dependencies: + pouchdb-changes-filter: 9.0.0 + pouchdb-errors: 9.0.0 + pouchdb-fetch: 9.0.0 + pouchdb-merge: 9.0.0 + pouchdb-utils: 9.0.0 + uuid: 8.3.2 + transitivePeerDependencies: + - encoding + + pouchdb-errors@9.0.0: {} + + pouchdb-fetch@9.0.0: + dependencies: + fetch-cookie: 2.2.0 + node-fetch: 2.6.9 + transitivePeerDependencies: + - encoding + + pouchdb-json@9.0.0: + dependencies: + vuvuzela: 1.0.3 + + pouchdb-md5@9.0.0: + dependencies: + pouchdb-binary-utils: 9.0.0 + spark-md5: 3.0.2 + + pouchdb-merge@9.0.0: + dependencies: + pouchdb-utils: 9.0.0 + + pouchdb-selector-core@9.0.0: + dependencies: + pouchdb-collate: 9.0.0 + pouchdb-utils: 9.0.0 + + pouchdb-utils@9.0.0: + dependencies: + pouchdb-errors: 9.0.0 + pouchdb-md5: 9.0.0 + uuid: 8.3.2 + + pouchdb@9.0.0: + dependencies: + double-ended-queue: 2.1.0-0 + fetch-cookie: 2.2.0 + level: 6.0.1 + level-codec: 9.0.2 + level-write-stream: 1.0.0 + leveldown: 6.1.1 + levelup: 4.4.0 + ltgt: 2.2.1 + node-fetch: 2.6.9 + readable-stream: 1.1.14 + spark-md5: 3.0.2 + through2: 3.0.2 + uuid: 8.3.2 + vuvuzela: 1.0.3 + transitivePeerDependencies: + - encoding + + prismjs@1.30.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-information@6.5.0: {} + + property-information@7.1.0: {} + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 24.7.2 + long: 5.3.2 + + prr@1.0.1: {} + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + + querystringify@2.2.0: {} + + queue-microtask@1.2.3: {} + + radix3@1.1.2: {} + + range-parser@1.2.1: {} + + react-dom@19.2.0(react@19.2.0): + dependencies: + react: 19.2.0 + scheduler: 0.27.0 + + react-i18next@15.7.4(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3): + dependencies: + '@babel/runtime': 7.28.4 + html-parse-stringify: 3.0.1 + i18next: 25.6.0(typescript@5.9.3) + react: 19.2.0 + optionalDependencies: + react-dom: 19.2.0(react@19.2.0) + typescript: 5.9.3 + + react-is@16.13.1: {} + + react-lifecycles-compat@3.0.4: {} + + react-modal@3.16.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + exenv: 1.2.2 + prop-types: 15.8.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-lifecycles-compat: 3.0.4 + warning: 4.0.3 + + react-refresh@0.17.0: {} + + react-resizable-panels@3.0.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + react-toastify@11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + clsx: 2.1.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + react-transition-state@2.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + react-virtualized@9.22.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + '@babel/runtime': 7.28.4 + clsx: 1.2.1 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-lifecycles-compat: 3.0.4 + + react@19.2.0: {} + + readable-stream@0.0.4: {} + + readable-stream@1.1.14: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@4.1.2: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.1(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.8 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + requires-port@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restructure@3.0.2: {} + + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.0.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + + reusify@1.1.0: {} + + rollup-plugin-dts@6.2.3(rollup@4.52.4)(typescript@5.9.3): + dependencies: + magic-string: 0.30.19 + rollup: 4.52.4 + typescript: 5.9.3 + optionalDependencies: + '@babel/code-frame': 7.27.1 + + rollup@4.52.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.4 + '@rollup/rollup-android-arm64': 4.52.4 + '@rollup/rollup-darwin-arm64': 4.52.4 + '@rollup/rollup-darwin-x64': 4.52.4 + '@rollup/rollup-freebsd-arm64': 4.52.4 + '@rollup/rollup-freebsd-x64': 4.52.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 + '@rollup/rollup-linux-arm-musleabihf': 4.52.4 + '@rollup/rollup-linux-arm64-gnu': 4.52.4 + '@rollup/rollup-linux-arm64-musl': 4.52.4 + '@rollup/rollup-linux-loong64-gnu': 4.52.4 + '@rollup/rollup-linux-ppc64-gnu': 4.52.4 + '@rollup/rollup-linux-riscv64-gnu': 4.52.4 + '@rollup/rollup-linux-riscv64-musl': 4.52.4 + '@rollup/rollup-linux-s390x-gnu': 4.52.4 + '@rollup/rollup-linux-x64-gnu': 4.52.4 + '@rollup/rollup-linux-x64-musl': 4.52.4 + '@rollup/rollup-openharmony-arm64': 4.52.4 + '@rollup/rollup-win32-arm64-msvc': 4.52.4 + '@rollup/rollup-win32-ia32-msvc': 4.52.4 + '@rollup/rollup-win32-x64-gnu': 4.52.4 + '@rollup/rollup-win32-x64-msvc': 4.52.4 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + sax@1.4.1: {} + + scheduler@0.27.0: {} + + selfsigned@3.0.1: + dependencies: + node-forge: 1.3.1 + + semver@6.3.1: {} + + semver@7.7.3: {} + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + set-cookie-parser@2.7.1: {} + + setprototypeof@1.2.0: {} + + sharp@0.34.4: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 + optional: true + + shiki@3.13.0: + dependencies: + '@shikijs/core': 3.13.0 + '@shikijs/engine-javascript': 3.13.0 + '@shikijs/engine-oniguruma': 3.13.0 + '@shikijs/langs': 3.13.0 + '@shikijs/themes': 3.13.0 + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + sigma@3.0.2(graphology-types@0.24.8): + dependencies: + events: 3.3.0 + graphology-utils: 2.5.2(graphology-types@0.24.8) + transitivePeerDependencies: + - graphology-types + + sisteransi@1.0.5: {} + + sitemap@8.0.0: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + + smol-toml@1.4.2: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@2.0.2: {} + + spark-md5@3.0.2: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + stream-replace-string@2.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string_decoder@0.10.31: {} + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + style-to-js@1.1.18: + dependencies: + style-to-object: 1.0.11 + + style-to-object@1.0.11: + dependencies: + inline-style-parser: 0.2.4 + + sublevel-pouchdb@9.0.0: + dependencies: + level-codec: 9.0.2 + ltgt: 2.2.1 + readable-stream: 1.1.14 + + supports-preserve-symlinks-flag@1.0.0: {} + + tabbable@6.2.0: {} + + tailwind-merge@3.3.1: {} + + tailwindcss@4.1.14: {} + + tapable@2.3.0: {} + + tar@7.5.1: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + + three@0.180.0: {} + + through2@3.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + tiny-inflate@1.0.3: {} + + tinyexec@1.0.1: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + turbo-darwin-64@2.5.8: + optional: true + + turbo-darwin-arm64@2.5.8: + optional: true + + turbo-linux-64@2.5.8: + optional: true + + turbo-linux-arm64@2.5.8: + optional: true + + turbo-windows-64@2.5.8: + optional: true + + turbo-windows-arm64@2.5.8: + optional: true + + turbo@2.5.8: + optionalDependencies: + turbo-darwin-64: 2.5.8 + turbo-darwin-arm64: 2.5.8 + turbo-linux-64: 2.5.8 + turbo-linux-arm64: 2.5.8 + turbo-windows-64: 2.5.8 + turbo-windows-arm64: 2.5.8 + + tw-animate-css@1.4.0: {} + + type-fest@4.41.0: {} + + typescript@5.9.3: {} + + ufo@1.6.1: {} + + ultrahtml@1.6.0: {} + + uncrypto@0.1.3: {} + + undici-types@7.14.0: {} + + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.6.0: + dependencies: + css-tree: 3.1.0 + ofetch: 1.4.1 + ohash: 2.0.11 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + universalify@0.2.0: {} + + unstorage@1.17.1(idb-keyval@6.2.2): + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.4 + lru-cache: 10.4.3 + node-fetch-native: 1.6.7 + ofetch: 1.4.1 + ufo: 1.6.1 + optionalDependencies: + idb-keyval: 6.2.2 + + update-browserslist-db@1.1.3(browserslist@4.26.3): + dependencies: + browserslist: 4.26.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + util-deprecate@1.0.2: {} + + uuid@8.3.2: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.4 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.7.2 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + + vitefu@1.1.1(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)): + optionalDependencies: + vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1) + + void-elements@3.1.0: {} + + vuvuzela@1.0.3: {} + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + wavesurfer.js@7.11.0: {} + + web-namespaces@2.0.1: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-pm-runs@1.1.0: {} + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + write-stream@0.4.3: + dependencies: + readable-stream: 0.0.4 + + xtend@4.0.2: {} + + xxhash-wasm@1.1.0: {} + + yallist@3.1.1: {} + + yallist@5.0.0: {} + + yargs-parser@21.1.1: {} + + yocto-queue@1.2.1: {} + + yocto-spinner@0.2.3: + dependencies: + yoctocolors: 2.1.2 + + yoctocolors@2.1.2: {} + + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76): + dependencies: + typescript: 5.9.3 + zod: 3.25.76 + + zod@3.25.76: {} + + zustand@5.0.8(@types/react@19.2.2)(react@19.2.0): + optionalDependencies: + '@types/react': 19.2.2 + react: 19.2.0 + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..b131d30 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,8 @@ +packages: + - web + - server + +onlyBuiltDependencies: + - '@tailwindcss/oxide' + - esbuild + - sharp diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..6f3fdfc --- /dev/null +++ b/readme.md @@ -0,0 +1,8 @@ +# light-code + +[介绍](https://docmost.xiongxiao.me/share/vh4ff05fru/p/2025-10-15-light-code-HyZh9ERDN1) + + +## 文件管理工具 + +[filebrowser](https://github.com/filebrowser/filebrowser/releases) \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..58768e5 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,25 @@ +node_modules + +dist + +app.config.json5 + +apps.config.json + +deploy.tar.gz +cache-file + +/apps + +logs + +release/* +!release/.gitkeep + +.turbo + +.env* +!.env.example + +pack-dist +app.config.json5.envision diff --git a/server/bun.config.ts b/server/bun.config.ts new file mode 100644 index 0000000..54a8d78 --- /dev/null +++ b/server/bun.config.ts @@ -0,0 +1,22 @@ +// @ts-check +import { resolvePath } from '@kevisual/use-config'; +import { execSync } from 'node:child_process'; + +const entry = 'src/index.ts'; +const naming = 'app'; +const external = ['sequelize', 'pg', 'ioredis', 'pm2']; +/** + * @type {import('bun').BuildConfig} + */ +// @ts-ignore +await Bun.build({ + target: 'node', + format: 'esm', + entrypoints: [resolvePath(entry, { meta: import.meta })], + outdir: resolvePath('./dist', { meta: import.meta }), + naming: { + entry: `${naming}.js`, + }, + external, + env: 'KEVISUAL_*', +}); diff --git a/server/code/root/light-code-demo/main.ts b/server/code/root/light-code-demo/main.ts new file mode 100644 index 0000000..b3b305e --- /dev/null +++ b/server/code/root/light-code-demo/main.ts @@ -0,0 +1,24 @@ +import { QueryRouterServer } from "@kevisual/router"; + +const app = new QueryRouterServer(); + +app.route({ + path: 'main' +}).define(async (ctx) => { + ctx.body = { + message: 'this is main. filename: root/light-code-demo/main.ts', + params: ctx.query + } +}).addTo(app) + +app.route({ + path: 'main2' +}).define(async (ctx) => { + ctx.body = { + message: 'this is main2. filename: root/light-code-demo/main.ts', + params: ctx.query + } +}).addTo(app) + + +app.wait() \ No newline at end of file diff --git a/server/code/root/light-code-demo/sign.ts b/server/code/root/light-code-demo/sign.ts new file mode 100644 index 0000000..a7c31cb --- /dev/null +++ b/server/code/root/light-code-demo/sign.ts @@ -0,0 +1,46 @@ +import { QueryRouterServer as Mini } from "@kevisual/router"; +import { NocoApi } from "@kevisual/noco"; +const config = { + NOCODB_URL: process.env.NOCODB_URL || 'https://nocodb.xiongxiao.me', + NOCODB_API_KEY: process.env.NOCODB_API_KEY || 'uca1Zx3p_0pnNUBV6ot9mBP6JCPqQ0X1TF3N3R7s' +} +const table = 'mcby44q8zrayvn9' +const nocoAPi = new NocoApi({ + baseURL: config.NOCODB_URL, + token: config.NOCODB_API_KEY, + table, +}); +console.log('nocoAPi', await nocoAPi.record.list()) +const app = new Mini(); + + +app.route({ + path: 'sign' +}).define(async (ctx) => { + const { Title, Description } = ctx.query + // 这里可以处理签到 + await nocoAPi.record.create({ Title, Description }) + const list = await nocoAPi.record.list({ sort: '-CreatedAt' }) + ctx.body = { message: '签到成功', list } +}).addTo(app) + +app.route({ + path: 'sign', + key: 'list' +}).define(async (ctx) => { + // 这里可以处理签到 + ctx.body = await nocoAPi.record.list() +}).addTo(app) + +app.route({ + path: 'sign', + key: 'delete' +}).define(async (ctx) => { + const { id } = ctx.query + // 这里可以处理签到 + await nocoAPi.record.delete({ Id: id }) + const list = await nocoAPi.record.list({ sort: '-CreatedAt' }) + ctx.body = { message: '删除成功', list } +}).addTo(app) + +app.wait() \ No newline at end of file diff --git a/server/code/root/light-code-demo/weather.ts b/server/code/root/light-code-demo/weather.ts new file mode 100644 index 0000000..ac826b5 --- /dev/null +++ b/server/code/root/light-code-demo/weather.ts @@ -0,0 +1,56 @@ +const weatherHost = 'n65khufe5n.re.qweatherapi.com'; +const token = 'fdad5aeb2ba54949a8a1df2a0f3d1efb' +const xihu = '101210113'; // 西湖 +export const getWeather = async (location: string = xihu) => { + const url = `https://${weatherHost}/v7/weather/3d?location=${location}`; + const headers = { + 'Authorization': `Bearer ${token}` + }; + const res = await fetch(url, { headers }); + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + const data = await res.json(); + return data; +} +// getWeather().then(console.log).catch(console.error); + +// https://dev.qweather.com/ +class Weather { + host: string; + token: string; + constructor(opts: { host: string; token: string }) { + this.host = opts.host; + this.token = opts.token; + console.log(this.host, this.token); + } + getWeather(location: string) { + return fetch(`https://${this.host}/v7/weather/now?location=${location}`, { + headers: { + 'Content-Type': 'application/json', + 'X-QW-Api-Key': ''.replace('', this.token), + }, + }).then((res) => res.json()); + } +} +const newWeather = new Weather({ + host: process.env?.QWEATHER_HOST || weatherHost, + token: process.env?.QWEATHER_TOKEN || token, +}); + + +// newWeather.getWeather(xihu).then(console.log).catch(console.error); + + +import { QueryRouterServer as Mini } from "@kevisual/router"; + +const app = new Mini(); + +app.route({ + path: 'main' +}).define(async (ctx) => { + ctx.body = await newWeather.getWeather(xihu); +}).addTo(app) + + +app.wait() \ No newline at end of file diff --git a/server/code/root/listen-demo/origin.ts b/server/code/root/listen-demo/origin.ts new file mode 100644 index 0000000..d1ef059 --- /dev/null +++ b/server/code/root/listen-demo/origin.ts @@ -0,0 +1,57 @@ +const main = () => { + const random = Math.random().toString(36).slice(-6) + return 'hello a ' + random +} + +// fs.writeFileSync('./a.txt', main() + '\n', { flag: 'a' }) +console.log('pwd', process.cwd()) +// const value = fs.readFileSync('./bun.config.ts', 'utf-8') +// console.log('a.txt 内容:', value) +const listen = async () => { + console.log('子进程启动,等待消息...') + + const getParams = async () => { + return new Promise((resolve) => { + process.on('message', (msg) => { + console.log('子进程收到消息:', msg) + resolve(msg) + }) + }) + } + + try { + const params = await getParams() + console.log('处理参数:', params) + + // 执行主要逻辑 + const result = main() + + // 发送结果回主进程 + const response = { + foo: 'bar', + params, + success: true, + data: { code: 200, data: result }, + timestamp: new Date().toISOString() + } + + process.send?.(response, (error) => { + if (error) { + console.error('发送消息失败:', error) + } else { + console.log('成功发送响应:', response) + } + process.exit(0) + }) + } catch (error) { + console.error('子进程执行出错:', error) + process.send?.({ + success: false, + error: error.message + }) + process.exit(1) + } +} + +// 启动监听 +listen().catch(console.error) \ No newline at end of file diff --git a/server/code/root/listen-demo/router.ts b/server/code/root/listen-demo/router.ts new file mode 100644 index 0000000..019e8ce --- /dev/null +++ b/server/code/root/listen-demo/router.ts @@ -0,0 +1,14 @@ +import { QueryRouterServer } from "@kevisual/router"; + +const app = new QueryRouterServer(); + +app.route({ + path: 'main' +}).define(async (ctx) => { + ctx.body = { + message: 'this is main. filename: root/listen-demo/router.ts', + params: ctx.query + } +}).addTo(app) + +app.wait() \ No newline at end of file diff --git a/server/code/test/demo/main.ts b/server/code/test/demo/main.ts new file mode 100644 index 0000000..09340d9 --- /dev/null +++ b/server/code/test/demo/main.ts @@ -0,0 +1,14 @@ +import { QueryRouterServer } from "@kevisual/router"; + +const app = new QueryRouterServer(); + +app.route({ + path: 'main' +}).define(async (ctx) => { + ctx.body = { + message: 'this is main. filename: test/demo/main.ts', + params: ctx.query + } +}).addTo(app) + +app.wait() \ No newline at end of file diff --git a/server/demo/test/a/index.html b/server/demo/test/a/index.html new file mode 100644 index 0000000..c0f7c68 --- /dev/null +++ b/server/demo/test/a/index.html @@ -0,0 +1 @@ +测试静态页面 \ No newline at end of file diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..f0c2991 --- /dev/null +++ b/server/package.json @@ -0,0 +1,31 @@ +{ + "name": "light-code", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "dev": "bun run --watch --hot src/index.ts", + "start": "bun run src/index.ts", + "prestart": "bun src/test/check-code.ts", + "build": "NODE_ENV=production bun bun.config.ts" + }, + "keywords": [], + "author": "abearxiong (https://www.xiongxiao.me)", + "license": "MIT", + "packageManager": "pnpm@10.16.1", + "type": "module", + "devDependencies": { + "@kevisual/local-proxy": "^0.0.6", + "@kevisual/types": "^0.0.10", + "@kevisual/use-config": "^1.0.19", + "@types/bun": "^1.3.0" + }, + "dependencies": { + "@kevisual/noco": "^0.0.1", + "@kevisual/query": "^0.0.29", + "@kevisual/router": "^0.0.29", + "fast-glob": "^3.3.3", + "pocketbase": "^0.26.2", + "unstorage": "^1.17.1" + } +} \ No newline at end of file diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml new file mode 100644 index 0000000..8574d98 --- /dev/null +++ b/server/pnpm-lock.yaml @@ -0,0 +1,384 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@kevisual/router': + specifier: ^0.0.28 + version: 0.0.28 + devDependencies: + '@kevisual/types': + specifier: ^0.0.10 + version: 0.0.10 + '@kevisual/use-config': + specifier: ^1.0.19 + version: 1.0.19(dotenv@16.6.1) + '@types/bun': + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.2) + bun: + specifier: ^1.3.0 + version: 1.3.0 + +packages: + + '@kevisual/load@0.0.6': + resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} + + '@kevisual/router@0.0.28': + resolution: {integrity: sha512-MqpnRqBRt2TkM9KyDDaz/AjbBFi8L2y2/MwChu28fK6g0OL5fJ45NQQBGNpNrj2rsUVmpCA2wDr2SqjVxE3CLA==} + + '@kevisual/types@0.0.10': + resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==} + + '@kevisual/use-config@1.0.19': + resolution: {integrity: sha512-Q1IH4eMqUe5w6Bq8etoqOSls9FPIy0xwwD3wHf26EsQLZadhccI9qkDuFzP/rFWDa57mwFPEfwbGE5UlqWOCkw==} + peerDependencies: + dotenv: ^16.4.7 + + '@oven/bun-darwin-aarch64@1.3.0': + resolution: {integrity: sha512-WeXSaL29ylJEZMYHHW28QZ6rgAbxQ1KuNSZD9gvd3fPlo0s6s2PglvPArjjP07nmvIK9m4OffN0k4M98O7WmAg==} + cpu: [arm64] + os: [darwin] + + '@oven/bun-darwin-x64-baseline@1.3.0': + resolution: {integrity: sha512-+FSr/ub5vA/EkD3fMhHJUzYioSf/sXd50OGxNDAntVxcDu4tXL/81Ka3R/gkZmjznpLFIzovU/1Ts+b7dlkrfw==} + cpu: [x64] + os: [darwin] + + '@oven/bun-darwin-x64@1.3.0': + resolution: {integrity: sha512-CFKjoUWQH0Oz3UHYfKbdKLq0wGryrFsTJEYq839qAwHQSECvVZYAnxVVDYUDa0yQFonhO2qSHY41f6HK+b7xtw==} + cpu: [x64] + os: [darwin] + + '@oven/bun-linux-aarch64-musl@1.3.0': + resolution: {integrity: sha512-HT5sr7N8NDYbQRjAnT7ISpx64y+ewZZRQozOJb0+KQObKvg4UUNXGm4Pn1xA4/WPMZDDazjO8E2vtOQw1nJlAQ==} + cpu: [arm64] + os: [linux] + + '@oven/bun-linux-aarch64@1.3.0': + resolution: {integrity: sha512-WHthS/eLkCNcp9pk4W8aubRl9fIUgt2XhHyLrP0GClB1FVvmodu/zIOtG0NXNpzlzB8+gglOkGo4dPjfVf4Z+g==} + cpu: [arm64] + os: [linux] + + '@oven/bun-linux-x64-baseline@1.3.0': + resolution: {integrity: sha512-OmlEH3nlxQyv7HOvTH21vyNAZGv9DIPnrTznzvKiOQxkOphhCyKvPTlF13ydw4s/i18iwaUrhHy+YG9HSSxa4Q==} + cpu: [x64] + os: [linux] + + '@oven/bun-linux-x64-musl-baseline@1.3.0': + resolution: {integrity: sha512-hrr7mDvUjMX1tuJaXz448tMsgKIqGJBY8+rJqztKOw1U5+a/v2w5HuIIW1ce7ut0ZwEn+KIDvAujlPvpH33vpQ==} + cpu: [x64] + os: [linux] + + '@oven/bun-linux-x64-musl@1.3.0': + resolution: {integrity: sha512-rtzUEzCynl3Rhgn/iR9DQezSFiZMcAXAbU+xfROqsweMGKwvwIA2ckyyckO08psEP8XcUZTs3LT9CH7PnaMiEA==} + cpu: [x64] + os: [linux] + + '@oven/bun-linux-x64@1.3.0': + resolution: {integrity: sha512-sGEWoJQXO4GDr0x4t/yJQ/Bq1yNkOdX9tHbZZ+DBGJt3z3r7jeb4Digv8xQUk6gdTFC9vnGHuin+KW3/yD1Aww==} + cpu: [x64] + os: [linux] + + '@oven/bun-windows-x64-baseline@1.3.0': + resolution: {integrity: sha512-/jVZ8eYjpYHLDFNoT86cP+AjuWvpkzFY+0R0a1bdeu0sQ6ILuy1FV6hz1hUAP390E09VCo5oP76fnx29giHTtA==} + cpu: [x64] + os: [win32] + + '@oven/bun-windows-x64@1.3.0': + resolution: {integrity: sha512-xXwtpZVVP7T+vkxcF/TUVVOGRjEfkByO4mKveKYb4xnHWV4u4NnV0oNmzyMKkvmj10to5j2h0oZxA4ZVVv4gfA==} + cpu: [x64] + os: [win32] + + '@types/bun@1.3.0': + resolution: {integrity: sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA==} + + '@types/node@24.7.2': + resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==} + + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + + bun-types@1.3.0: + resolution: {integrity: sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ==} + peerDependencies: + '@types/react': ^19 + + bun@1.3.0: + resolution: {integrity: sha512-YI7mFs7iWc/VsGsh2aw6eAPD2cjzn1j+LKdYVk09x1CrdTWKYIHyd+dG5iQoN9//3hCDoZj8U6vKpZzEf5UARA==} + cpu: [arm64, x64] + os: [darwin, linux, win32] + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + selfsigned@3.0.1: + resolution: {integrity: sha512-6U6w6kSLrM9Zxo0D7mC7QdGS6ZZytMWBnj/vhF9p+dAHx6CwGezuRcO4VclTbrrI7mg7SD6zNiqXUuBHOVopNQ==} + engines: {node: '>=10'} + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} + +snapshots: + + '@kevisual/load@0.0.6': + dependencies: + eventemitter3: 5.0.1 + + '@kevisual/router@0.0.28': + dependencies: + path-to-regexp: 8.3.0 + selfsigned: 3.0.1 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + '@kevisual/types@0.0.10': {} + + '@kevisual/use-config@1.0.19(dotenv@16.6.1)': + dependencies: + '@kevisual/load': 0.0.6 + dotenv: 16.6.1 + + '@oven/bun-darwin-aarch64@1.3.0': + optional: true + + '@oven/bun-darwin-x64-baseline@1.3.0': + optional: true + + '@oven/bun-darwin-x64@1.3.0': + optional: true + + '@oven/bun-linux-aarch64-musl@1.3.0': + optional: true + + '@oven/bun-linux-aarch64@1.3.0': + optional: true + + '@oven/bun-linux-x64-baseline@1.3.0': + optional: true + + '@oven/bun-linux-x64-musl-baseline@1.3.0': + optional: true + + '@oven/bun-linux-x64-musl@1.3.0': + optional: true + + '@oven/bun-linux-x64@1.3.0': + optional: true + + '@oven/bun-windows-x64-baseline@1.3.0': + optional: true + + '@oven/bun-windows-x64@1.3.0': + optional: true + + '@types/bun@1.3.0(@types/react@19.2.2)': + dependencies: + bun-types: 1.3.0(@types/react@19.2.2) + transitivePeerDependencies: + - '@types/react' + + '@types/node@24.7.2': + dependencies: + undici-types: 7.14.0 + + '@types/react@19.2.2': + dependencies: + csstype: 3.1.3 + + bun-types@1.3.0(@types/react@19.2.2): + dependencies: + '@types/node': 24.7.2 + '@types/react': 19.2.2 + + bun@1.3.0: + optionalDependencies: + '@oven/bun-darwin-aarch64': 1.3.0 + '@oven/bun-darwin-x64': 1.3.0 + '@oven/bun-darwin-x64-baseline': 1.3.0 + '@oven/bun-linux-aarch64': 1.3.0 + '@oven/bun-linux-aarch64-musl': 1.3.0 + '@oven/bun-linux-x64': 1.3.0 + '@oven/bun-linux-x64-baseline': 1.3.0 + '@oven/bun-linux-x64-musl': 1.3.0 + '@oven/bun-linux-x64-musl-baseline': 1.3.0 + '@oven/bun-windows-x64': 1.3.0 + '@oven/bun-windows-x64-baseline': 1.3.0 + + csstype@3.1.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + depd@2.0.0: {} + + dotenv@16.6.1: {} + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + eventemitter3@5.0.1: {} + + fresh@2.0.0: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + inherits@2.0.4: {} + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + ms@2.1.3: {} + + node-forge@1.3.1: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + path-to-regexp@8.3.0: {} + + range-parser@1.2.1: {} + + selfsigned@3.0.1: + dependencies: + node-forge: 1.3.1 + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + toidentifier@1.0.1: {} + + undici-types@7.14.0: {} diff --git a/server/src/app.ts b/server/src/app.ts new file mode 100644 index 0000000..d62a8db --- /dev/null +++ b/server/src/app.ts @@ -0,0 +1,3 @@ +import { App } from '@kevisual/router' + +export const app = new App() diff --git a/server/src/cache/index.ts b/server/src/cache/index.ts new file mode 100644 index 0000000..768001d --- /dev/null +++ b/server/src/cache/index.ts @@ -0,0 +1,23 @@ +import { createStorage } from 'unstorage' +import fsLiteDriver from "unstorage/drivers/fs-lite"; +import { codeRoot } from '@/modules/config.ts'; +import memoryDriver from "unstorage/drivers/memory"; + +export const storage = createStorage({ + // @ts-ignore + driver: memoryDriver(), +}); + +export const codeStorage = createStorage({ + // @ts-ignore + driver: fsLiteDriver({ + base: codeRoot + }) +}) + +// storage.setItem('test-ke/test-key.json', 'test-value'); +// console.log('Cache test-key:', await storage.getItem('test-key')); + +// codeStorage.setItem('root/light-code-demo/main.ts', 'test-value2'); +console.log('Cache test-key:', await codeStorage.getItem('root/light-code-demo/main.ts')); +console.log('has', await codeStorage.hasItem('root/light-code-demo/main.ts')); diff --git a/server/src/db/collections/project.ts b/server/src/db/collections/project.ts new file mode 100644 index 0000000..b4ae1d5 --- /dev/null +++ b/server/src/db/collections/project.ts @@ -0,0 +1,52 @@ + +import { Field } from '../types/index.ts'; + +export const projectStatus = ['提交', '审核中', '审核通过']; // 提交,审核中,审核通过 +export type projectStatus = typeof projectStatus[number]; + +export const projectFields: Field[] = [ + { + name: 'title', + type: 'text', + }, + { + name: 'description', + type: 'text', + }, + { + name: 'status', + type: 'text' + }, + { + name: 'key', + type: 'text', + }, + { + name: 'owner', + type: 'text', + }, + { + name: 'data', + type: 'json' + }, + { + name: 'files', + type: 'json' + }, + { + name: "createdAt", + onCreate: true, + onUpdate: false, + type: "autodate" + }, + { + name: "updatedAt", + onCreate: true, + onUpdate: true, + type: "autodate" + }, +]; + +export const name = 'xx_projects'; + +export const type = 'base'; \ No newline at end of file diff --git a/server/src/db/init.ts b/server/src/db/init.ts new file mode 100644 index 0000000..cbc206a --- /dev/null +++ b/server/src/db/init.ts @@ -0,0 +1,37 @@ +import { pb, db } from '../modules/db.ts' +import * as projects from './collections/project.ts' +// 要求 +// 1. collection只做新增,不做修改 +// 2. collection不存在就创建 +// 3. 每一个collection的定义在文档中需要有 + + +export const main = async () => { + try { + await db.ensureLogin().catch(() => { throw new Error('Login failed'); }); + // 程序第一次运行的时候执行,如果已经初始化过则跳过 + const collections = await db.pb.collections.getFullList({ + filter: 'name ~ "xx_%"', + }) + console.log('Existing collections:', collections.map(c => c.name)); + const dbs = [projects] + for (const coll of dbs) { + const exists = collections.find(c => c.name === coll.name) + if (exists) { + console.log(`Collection ${coll.name} already exists, skipping creation.`); + continue; + } + // 第一步,获取那个叉叉开头的 Collection。第二步,获取它的版本。 + const createdCollection = await db.pb.collections.create({ + name: coll.name, + type: coll?.type || 'base', + fields: coll?.projectFields, + }) + console.log('Created collection:', createdCollection); + } + + } catch (error) { + console.error('Error during DB initialization:', error); + } +} +main() diff --git a/server/src/db/types/collection.ts b/server/src/db/types/collection.ts new file mode 100644 index 0000000..742c047 --- /dev/null +++ b/server/src/db/types/collection.ts @@ -0,0 +1,26 @@ +export type Field = { + /** + * The unique identifier for the field + */ + id?: string; + /** + * The name of the field + */ + name: string; + type?: 'text' | 'json' | 'autodate' | 'boolean' | 'number' | 'email' | 'url' | 'file' | 'relation'; + /** + * Indicates whether the field is required + */ + required?: boolean; + /** + * Only for 'autodate' type + * Indicates whether to set the date on record creation or update + */ + onCreate?: boolean; + /** Only for 'autodate' type + * Indicates whether to set the date on record update + */ + onUpdate?: boolean; + options?: Record; + [key: string]: any; +} \ No newline at end of file diff --git a/server/src/db/types/index.ts b/server/src/db/types/index.ts new file mode 100644 index 0000000..df8b40f --- /dev/null +++ b/server/src/db/types/index.ts @@ -0,0 +1 @@ +export * from './collection.ts'; \ No newline at end of file diff --git a/server/src/index.ts b/server/src/index.ts new file mode 100644 index 0000000..2bb4e29 --- /dev/null +++ b/server/src/index.ts @@ -0,0 +1,19 @@ +import { proxyRoute, initProxy } from '@kevisual/local-proxy/proxy.ts'; +// http://localhost:4005/test/a/index.html +initProxy({ + pagesDir: './demo', + watch: true, + home: '/root/light-code-center', +}); + +import { app } from './app.ts' +import './routes/index.ts' + + +app.listen(4005, () => { + console.log('Server is running on http://localhost:4005') +}) + +app.onServerRequest(proxyRoute); + +export { app } \ No newline at end of file diff --git a/server/src/modules/config.ts b/server/src/modules/config.ts new file mode 100644 index 0000000..d9633e5 --- /dev/null +++ b/server/src/modules/config.ts @@ -0,0 +1,7 @@ +import { useConfig } from '@kevisual/use-config' + +import path from 'path'; + +export const config = useConfig() + +export const codeRoot = path.join(process.cwd(), 'code'); \ No newline at end of file diff --git a/server/src/modules/db.ts b/server/src/modules/db.ts new file mode 100644 index 0000000..ddb496a --- /dev/null +++ b/server/src/modules/db.ts @@ -0,0 +1,24 @@ +import PocketBase from 'pocketbase'; + +const POCKETBASE_URL = 'https://pocketbase.pro.xiongxiao.me/'; + +export const pb = new PocketBase(POCKETBASE_URL); + +export class DB { + pb: PocketBase + constructor(pb: PocketBase) { + this.pb = pb + } + async ensureLogin() { + const pb = this.pb; + if (!pb.authStore.isValid) { + await pb.collection("_superusers").authWithPassword('xiongxiao@xiongxiao.me', '123456xx'); + } + return pb.authStore.record; + } + async getCollection(name: string) { + await this.ensureLogin(); + return this.pb.collection(name); + } +} +export const db = new DB(pb); \ No newline at end of file diff --git a/server/src/modules/run-code/run.ts b/server/src/modules/run-code/run.ts new file mode 100644 index 0000000..7502da1 --- /dev/null +++ b/server/src/modules/run-code/run.ts @@ -0,0 +1,50 @@ +import { fork } from 'child_process' + +export type RunCodeParams = { + path?: string; + key?: string; + payload?: string; + [key: string]: any +} +type RunCode = { + // 调用进程的功能 + success?: boolean + data?: { + // 调用router的结果 + code?: number + data?: any + message?: string + [key: string]: any + }; + error?: any + timestamp?: string + [key: string]: any +} +export const runCode = async (tsPath: string, params: RunCodeParams = {}): Promise => { + return new Promise((resolve, reject) => { + // 使用 Bun 的 fork 模式启动子进程 + const child = fork(tsPath) + + // 监听来自子进程的消息 + child.on('message', (msg: RunCode) => { + resolve(msg) + }) + + // child.on('exit', (code, signal) => { + // console.log('子进程已退出,退出码:', code, '信号:', signal) + // }) + + // child.on('close', (code, signal) => { + // console.log('子进程已关闭,退出码:', code, '信号:', signal) + // }) + + child.on('error', (error) => { + resolve({ + success: false, error: error?.message + }) + }) + + // 向子进程发送消息 + child.send(params) + }); +} \ No newline at end of file diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts new file mode 100644 index 0000000..ec1f2fb --- /dev/null +++ b/server/src/routes/auth.ts @@ -0,0 +1,8 @@ +import { app } from '../app.ts' + +app.route({ + path: 'auth', + id: 'auth' +}).define(async (ctx) => { + // Authentication logic here +}).addTo(app); \ No newline at end of file diff --git a/server/src/routes/call/index.ts b/server/src/routes/call/index.ts new file mode 100644 index 0000000..b3489c7 --- /dev/null +++ b/server/src/routes/call/index.ts @@ -0,0 +1,24 @@ +import { app } from '../../app.ts' +import path from 'path' +import { runCode } from '../../modules/run-code/run.ts' + +// http://localhost:4005/api/router?path=call +app.route({ + path: 'call' +}).define(async (ctx) => { + const filename = ctx.query?.filename || 'root/listen-demo/router.ts' + const data = ctx.query?.data || {} + const pwd = process.cwd() + const testA = path.join(pwd, 'code', filename) + const resulst = await runCode(testA, data) + if (resulst.success) { + const callResult = resulst.data; + if (callResult.code === 200) ctx.body = callResult.data + else { + const callError = `调用程序错误: ${callResult.message}` + ctx.throw(callResult.code, callError) + } + } else { + ctx.body = `执行脚本错误: ${resulst.error}` + } +}).addTo(app) \ No newline at end of file diff --git a/server/src/routes/file-code/index.ts b/server/src/routes/file-code/index.ts new file mode 100644 index 0000000..4cffc47 --- /dev/null +++ b/server/src/routes/file-code/index.ts @@ -0,0 +1,68 @@ +import { app } from '@/app.ts'; +import path from 'node:path' +import glob from 'fast-glob'; +import fs from 'node:fs' +import { codeRoot } from '@/modules/config.ts'; +const list = async () => { + + const files = await glob('**/*.ts', { cwd: codeRoot }); + type FileContent = { + path: string; + content: string; + } + const filesContent: FileContent[] = []; + for (const file of files) { + if (file.startsWith('node_modules') || file.startsWith('dist') || file.startsWith('.git')) continue; + const fullPath = path.join(codeRoot, file); + const content = fs.readFileSync(fullPath, 'utf-8'); + if (content) { + filesContent.push({ path: file, content: content }); + } + } + return filesContent; +} + +app.route({ + path: 'file-code' +}).define(async (ctx) => { + const files = await list(); + ctx.body = files +}).addTo(app); + +type UploadProps = { + user: string; + key: string; + files: { + type: 'file' | 'base64'; + filepath: string; + content: string; + }[]; +} +app.route({ + path: 'file-code', + key: 'upload', + middleware: ['auth'] +}).define(async (ctx) => { + const upload = ctx.query?.upload as UploadProps; + if (!upload || !upload.user || !upload.key || !upload.files) { + ctx.throw(400, 'Invalid upload data'); + } + const user = upload.user; + const key = upload.key; + for (const file of upload.files) { + if (file.type === 'file') { + const fullPath = path.join(codeRoot, user, key, file.filepath); + const dir = path.dirname(fullPath); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(fullPath, file.content, 'utf-8'); + } else if (file.type === 'base64') { + const fullPath = path.join(codeRoot, user, key, file.filepath); + const dir = path.dirname(fullPath); + fs.mkdirSync(dir, { recursive: true }); + const buffer = Buffer.from(file.content, 'base64'); + fs.writeFileSync(fullPath, buffer); + } + } + ctx.body = { success: true }; + +}).addTo(app) \ No newline at end of file diff --git a/server/src/routes/index.ts b/server/src/routes/index.ts new file mode 100644 index 0000000..7f00dd1 --- /dev/null +++ b/server/src/routes/index.ts @@ -0,0 +1,5 @@ +import './call/index.ts'; + +import './file-code/index.ts'; + +import './auth.ts' \ No newline at end of file diff --git a/server/src/test/check-code.ts b/server/src/test/check-code.ts new file mode 100644 index 0000000..0e5b14b --- /dev/null +++ b/server/src/test/check-code.ts @@ -0,0 +1,37 @@ +import path from 'node:path' +import fs from 'node:fs' +const main = async () => { + const root = path.join(process.cwd(), 'code'); + const buckupRoot = path.join(process.cwd(), 'code-backup'); + + // 如果 code 文件夹不存在或文件夹列表长度等于0,则从 code-backup 复制 + let shouldCopy = false; + + if (!fs.existsSync(root)) { + console.log('code 文件夹不存在'); + shouldCopy = true; + } else { + // 检查 code 文件夹下的文件夹列表 + const items = await fs.promises.readdir(root, { withFileTypes: true }); + const folders = items.filter(item => item.isDirectory()); + + if (folders.length === 0) { + console.log('code 文件夹存在但为空(无子文件夹)'); + shouldCopy = true; + } else { + console.log(`code 文件夹已存在且包含 ${folders.length} 个子文件夹`); + } + } + + if (shouldCopy) { + if (fs.existsSync(buckupRoot)) { + console.log('正在从 code-backup 复制...'); + await fs.promises.cp(buckupRoot, root, { recursive: true }); + console.log('复制完成!'); + } else { + console.log('code-backup 文件夹不存在,无法复制'); + } + } +} + +main().catch(console.error) \ No newline at end of file diff --git a/server/src/test/common.ts b/server/src/test/common.ts new file mode 100644 index 0000000..3566829 --- /dev/null +++ b/server/src/test/common.ts @@ -0,0 +1,5 @@ +import { Query } from '@kevisual/query' + +export const query = new Query({ + url: 'http://localhost:4005/api/router', +}) \ No newline at end of file diff --git a/server/src/test/test-upload.ts b/server/src/test/test-upload.ts new file mode 100644 index 0000000..97a3a30 --- /dev/null +++ b/server/src/test/test-upload.ts @@ -0,0 +1,48 @@ +import { query } from './common.ts' + + + +export const testUpload = async () => { + + const res = await query.post({ + path: 'file-code', + key: 'upload', + upload: { + user: 'test', + key: 'demo', + files: [ + { + type: 'file', + filepath: 'main.ts', + content: `import { QueryRouterServer } from "@kevisual/router"; + +const app = new QueryRouterServer(); + +app.route({ + path: 'main' +}).define(async (ctx) => { + ctx.body = { + message: 'this is main. filename: test/demo/main.ts', + params: ctx.query + } +}).addTo(app) + +app.wait()` + }, + ] + } + }) + console.log('Upload response:', res); +} + +// testUpload(); + +const callTestDemo = async () => { + const res = await query.post({ + path: 'call', + filename: 'test/demo/main.ts', + }) + console.log('Call response:', res); +} + +callTestDemo(); \ No newline at end of file diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 0000000..9f27bb9 --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "@kevisual/types/json/backend.json", + "compilerOptions": { + "baseUrl": ".", + "module": "NodeNext", + "target": "esnext", + "typeRoots": [ + "./node_modules/@types" + ], + "paths": { + "@/*": [ + "src/*" + ] + }, + }, + "include": [ + "src/**/*", "code/**/*", + ], +} \ No newline at end of file diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..739efff --- /dev/null +++ b/turbo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "outputs": [ + "dist/**" + ] + }, + "dev": { + "cache": false + }, + "build:app": { + "outputs": [ + "dist/**" + ] + } + } +} \ No newline at end of file diff --git a/web/.env.example b/web/.env.example new file mode 100644 index 0000000..6d3973d --- /dev/null +++ b/web/.env.example @@ -0,0 +1,6 @@ +# PocketBase配置 +VITE_POCKETBASE_URL=http://localhost:8090 + +# 可选:其他配置 +# VITE_APP_NAME=Light Code Center +# VITE_DEBUG=true \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..1ee6af6 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,6 @@ +node_modules +.DS_Store + +.astro + +dist diff --git a/web/astro.config.mjs b/web/astro.config.mjs new file mode 100644 index 0000000..f1723d3 --- /dev/null +++ b/web/astro.config.mjs @@ -0,0 +1,36 @@ +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; +import react from '@astrojs/react'; +import sitemap from '@astrojs/sitemap'; +import pkgs from './package.json'; +import tailwindcss from '@tailwindcss/vite'; + +const isDev = process.env.NODE_ENV === 'development'; + +let target = process.env.VITE_API_URL || 'http://localhost:4005'; +const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' }; +let proxy = { + '/root/': { + target: `${target}/root/`, + }, + '/api': apiProxy, +}; + +export default defineConfig({ + base: isDev ? undefined : pkgs.basename, + integrations: [ + mdx(), + react(), // + // sitemap(), // sitemap must be site has a domain + ], + + vite: { + plugins: [tailwindcss()], + server: { + port: 7008, + host: '0.0.0.0', + allowedHosts: true, + proxy, + }, + }, +}); diff --git a/web/components.json b/web/components.json new file mode 100644 index 0000000..b84573c --- /dev/null +++ b/web/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/global.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..471832b --- /dev/null +++ b/web/package.json @@ -0,0 +1,82 @@ +{ + "name": "@kevisual/light-code-center", + "version": "0.0.1", + "description": "", + "main": "index.js", + "basename": "/root/light-code-center", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "pub": "envision deploy ./dist -k light-code-center -v 0.0.1 -u", + "sn": "pnpm dlx shadcn@latest add " + }, + "keywords": [], + "author": "abearxiong (https://www.xiongxiao.me)", + "license": "MIT", + "type": "module", + "dependencies": { + "@astrojs/mdx": "^4.3.7", + "@astrojs/react": "^4.4.0", + "@astrojs/sitemap": "^3.6.0", + "@faker-js/faker": "^10.1.0", + "@floating-ui/react": "^0.27.16", + "@kevisual/noco": "^0.0.1", + "@kevisual/query": "^0.0.29", + "@kevisual/query-login": "^0.0.6", + "@kevisual/registry": "^0.0.1", + "@ricky0123/vad-web": "^0.0.28", + "@szhsin/react-menu": "^4.5.0", + "@tailwindcss/vite": "^4.1.14", + "astro": "^5.14.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "dayjs": "^1.11.18", + "es-toolkit": "^1.40.0", + "events": "^3.3.0", + "graphology": "^0.26.0", + "highlight.js": "^11.11.1", + "lodash-es": "^4.17.21", + "lucide-react": "^0.545.0", + "marked": "^16.4.1", + "nanoid": "^5.1.6", + "pocketbase": "^0.26.2", + "pouchdb-adapter-memory": "^9.0.0", + "pouchdb-browser": "^9.0.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-modal": "^3.16.3", + "react-resizable-panels": "^3.0.6", + "react-toastify": "^11.0.5", + "react-virtualized": "^9.22.6", + "sigma": "^3.0.2", + "tailwind-merge": "^3.3.1", + "three": "^0.180.0", + "wavesurfer.js": "^7.11.0", + "zustand": "^5.0.8" + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@kevisual/types": "^0.0.10", + "@types/pouchdb": "^6.4.2", + "@types/pouchdb-browser": "^6.1.5", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "@types/react-modal": "^3.16.3", + "@types/react-virtualized": "^9.22.3", + "@types/three": "^0.180.0", + "@vitejs/plugin-basic-ssl": "^2.1.0", + "dotenv": "^17.2.3", + "pouchdb": "^9.0.0", + "tailwindcss": "^4.1.14", + "tw-animate-css": "^1.4.0" + }, + "packageManager": "pnpm@10.18.3", + "onlyBuiltDependencies": [ + "@tailwindcss/oxide", + "esbuild", + "sharp" + ] +} \ No newline at end of file diff --git a/web/src/apps/login/AuthProvider.tsx b/web/src/apps/login/AuthProvider.tsx new file mode 100644 index 0000000..3ab02e6 --- /dev/null +++ b/web/src/apps/login/AuthProvider.tsx @@ -0,0 +1,17 @@ +import React, { useEffect } from 'react'; +import { useAuthStore } from '@/store/authStore'; + +interface AuthProviderProps { + children: React.ReactNode; +} + +export const AuthProvider: React.FC = ({ children }) => { + const initAuth = useAuthStore(state => state.initAuth); + + useEffect(() => { + // 在应用启动时初始化认证状态 + initAuth(); + }, [initAuth]); + + return <>{children}; +}; \ No newline at end of file diff --git a/web/src/apps/login/DashboardApp.tsx b/web/src/apps/login/DashboardApp.tsx new file mode 100644 index 0000000..7ff4e92 --- /dev/null +++ b/web/src/apps/login/DashboardApp.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { AuthProvider } from './AuthProvider'; +import { ProtectedRoute } from '@/apps/login/ProtectedRoute'; +import { UserInfo } from '@/apps/login/UserInfo'; +import { useAuth } from '../../store/authStore'; +import { UserType } from '../../lib/pocketbase'; +import { ToastContainer } from 'react-toastify'; + +const DashboardContent: React.FC = () => { + const { user } = useAuth(); + + return ( +
+
+
+
+

用户仪表板

+ +
+
+
+ +
+
+
+
+

+ 欢迎,{user?.email || '用户'}! +

+ +
+
+

项目管理

+

管理您的代码项目和文件

+
+ +
+

设置

+

配置您的账户设置

+
+
+
+
+
+
+
+ ); +}; + +export const DashboardApp: React.FC = () => { + return ( + + + + + + + ); +}; \ No newline at end of file diff --git a/web/src/apps/login/LoginForm.tsx b/web/src/apps/login/LoginForm.tsx new file mode 100644 index 0000000..4c520dd --- /dev/null +++ b/web/src/apps/login/LoginForm.tsx @@ -0,0 +1,200 @@ +import React, { useState } from 'react'; +import { useAuthStore, useAuth, useAuthActions } from '@/store/authStore'; +import { UserType } from '@/lib/pocketbase'; +import { toast } from 'react-toastify'; + +interface LoginFormData { + email: string; + password: string; + userType: UserType; +} + +export const LoginForm: React.FC = () => { + const { isLoading, error } = useAuth(); + const { login, clearError } = useAuthActions(); + + const [formData, setFormData] = useState({ + email: '', + password: '', + userType: UserType.USER, + }); + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value, + })); + + // 清除错误信息 + if (error) { + clearError(); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!formData.email || !formData.password) { + toast.error('请填写邮箱和密码'); + return; + } + + try { + await login(formData); + toast.success('登录成功!'); + + // 登录成功后跳转到首页或仪表板 + window.location.href = formData.userType === UserType.ADMIN ? '/admin' : '/dashboard'; + } catch (error) { + toast.error(error instanceof Error ? error.message : '登录失败'); + } + }; + + const handleUserTypeChange = (userType: UserType) => { + setFormData(prev => ({ + ...prev, + userType, + })); + + if (error) { + clearError(); + } + }; + + return ( +
+
+
+

+ 登录您的账户 +

+

+ 选择您的账户类型并输入登录信息 +

+
+ +
+ {/* 用户类型选择 */} +
+
+ +
+ + +
+
+ + {/* 邮箱输入 */} +
+ + +
+ + {/* 密码输入 */} +
+ + +
+
+ + {/* 错误信息显示 */} + {error && ( +
+
+
+

+ {error} +

+
+
+
+ )} + + {/* 提交按钮 */} +
+ +
+ + {/* 提示信息 */} +
+

+ {formData.userType === UserType.ADMIN + ? '管理员账户将使用 superuser 权限登录' + : '普通用户账户将使用标准权限登录' + } +

+
+
+
+
+ ); +}; \ No newline at end of file diff --git a/web/src/apps/login/LoginPage.tsx b/web/src/apps/login/LoginPage.tsx new file mode 100644 index 0000000..6926442 --- /dev/null +++ b/web/src/apps/login/LoginPage.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { LoginForm } from './LoginForm'; +import { ToastContainer } from 'react-toastify'; + +export const LoginPage: React.FC = () => { + return ( + <> + + + + ); +}; \ No newline at end of file diff --git a/web/src/apps/login/ProtectedRoute.tsx b/web/src/apps/login/ProtectedRoute.tsx new file mode 100644 index 0000000..9c7ed2a --- /dev/null +++ b/web/src/apps/login/ProtectedRoute.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { useAuth, usePermissions } from '@/store/authStore'; +import { UserType } from '@/lib/pocketbase'; + +interface ProtectedRouteProps { + children: React.ReactNode; + requiredUserType?: UserType; + fallback?: React.ReactNode; + redirectTo?: string; +} + +export const ProtectedRoute: React.FC = ({ + children, + requiredUserType, + fallback, + redirectTo = '/login' +}) => { + const { isAuthenticated, isLoading } = useAuth(); + const { canAccess } = usePermissions(); + + // 加载中显示 + if (isLoading) { + return ( +
+
+
+ 验证身份中... +
+
+ ); + } + + // 未登录 + if (!isAuthenticated) { + if (fallback) { + return <>{fallback}; + } + + // 重定向到登录页面 + if (typeof window !== 'undefined') { + window.location.href = redirectTo; + } + + return ( +
+
+

需要登录

+

请先登录您的账户

+ + 前往登录 + +
+
+ ); + } + + // 检查权限 + if (requiredUserType && !canAccess(requiredUserType)) { + if (fallback) { + return <>{fallback}; + } + + return ( +
+
+

权限不足

+

+ 您需要{requiredUserType === UserType.ADMIN ? '管理员' : '用户'}权限才能访问此页面 +

+ + 返回首页 + +
+
+ ); + } + + return <>{children}; +}; + +// 专门用于管理员页面的保护组件 +export const AdminRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { + return ( + + {children} + + ); +}; + +// 专门用于普通用户页面的保护组件 +export const UserRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/web/src/apps/login/UserInfo.tsx b/web/src/apps/login/UserInfo.tsx new file mode 100644 index 0000000..674351e --- /dev/null +++ b/web/src/apps/login/UserInfo.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { useAuth, useAuthActions, usePermissions } from '@/store/authStore'; +import { UserType } from '@/lib/pocketbase'; +import { toast } from 'react-toastify'; + +export const UserInfo: React.FC = () => { + const { isAuthenticated, user, userType, isLoading } = useAuth(); + const { logout } = useAuthActions(); + const { isAdmin, isUser } = usePermissions(); + + const handleLogout = () => { + logout(); + toast.success('已成功登出'); + window.location.href = '/login'; + }; + + if (isLoading) { + return ( +
+
+ 加载中... +
+ ); + } + + if (!isAuthenticated || !user) { + return ( + + ); + } + + return ( +
+ {/* 用户信息 */} +
+ {/* 用户头像 */} +
+ {user.email?.charAt(0).toUpperCase() || 'U'} +
+ + {/* 用户详情 */} +
+ + {(user as any).name || (user as any).username || user.email} + + + {isAdmin ? '管理员' : '用户'} + +
+
+ + {/* 操作按钮 */} +
+ {/* 仪表板链接 */} + + {isAdmin ? '管理面板' : '仪表板'} + + + {/* 登出按钮 */} + +
+
+ ); +}; + +// 简化版本的用户状态显示组件 +export const UserStatus: React.FC = () => { + const { isAuthenticated, user, userType } = useAuth(); + const { isAdmin } = usePermissions(); + + if (!isAuthenticated || !user) { + return ( + 未登录 + ); + } + + return ( +
+
+ + {isAdmin ? '管理员' : '用户'}: {user.email} + +
+ ); +}; \ No newline at end of file diff --git a/web/src/apps/muse/base/card/MarkDetailList.css b/web/src/apps/muse/base/card/MarkDetailList.css new file mode 100644 index 0000000..59f7ca5 --- /dev/null +++ b/web/src/apps/muse/base/card/MarkDetailList.css @@ -0,0 +1,46 @@ +/* MarkDetailList 组件样式 */ + +/* 虚拟化列表容器 */ +.ReactVirtualized__List { + outline: none; +} + +/* 列表行容器 */ +.ReactVirtualized__List .ReactVirtualized__Grid__innerScrollContainer > div { + box-sizing: border-box; +} + +/* 防止内容溢出 */ +.mark-detail-row { + overflow: hidden; + box-sizing: border-box; +} + +/* 卡片样式优化 */ +.mark-detail-card { + margin-bottom: 8px; + box-sizing: border-box; + position: relative; + z-index: 1; +} + +/* 确保图片不会影响布局 */ +.mark-detail-card img { + flex-shrink: 0; +} + +/* 代码块样式优化 */ +.mark-detail-card pre { + word-wrap: break-word; + white-space: pre-wrap; +} + +/* 链接样式 */ +.mark-detail-card a { + word-break: break-all; +} + +/* 标签容器 */ +.mark-detail-tags { + min-height: 24px; +} diff --git a/web/src/apps/muse/base/card/MarkDetailList.tsx b/web/src/apps/muse/base/card/MarkDetailList.tsx new file mode 100644 index 0000000..f4b3ee2 --- /dev/null +++ b/web/src/apps/muse/base/card/MarkDetailList.tsx @@ -0,0 +1,364 @@ + +import React, { useState, useMemo } from 'react'; +import { AutoSizer, List } from 'react-virtualized'; +import './MarkDetailList.css'; +export type MarkShow = { + id: string; + title?: string; + description?: string; + tags?: string[]; + markType?: string; + cover?: string; + link?: string; + summary?: string; + key?: string; + data: any; + createdAt?: string; + updatedAt?: string; + markedAt?: Date; +} + +export type SimpleMarkShow = { + id: string; + title?: string; + description?: string; + tags?: string[]; + cover?: string; + link?: string; + summary?: string; +} + +interface MarkDetailProps { + data: MarkShow[]; +} + +export const MarkDetailList: React.FC = ({ data = [] }) => { + const [showAll, setShowAll] = useState(false); + + // 根据显示模式过滤数据 + const displayData = useMemo(() => { + if (showAll) { + // 显示所有字段 + return data; + } else { + // 仅显示 SimpleMarkShow 字段 + return data.map(item => ({ + id: item.id, + title: item.title, + description: item.description, + tags: item.tags, + cover: item.cover, + link: item.link, + summary: item.summary + } as SimpleMarkShow)); + } + }, [data, showAll]); + + // 计算行高 - 根据视图模式计算固定高度 + const getRowHeight = () => { + return showAll ? 400 : 240; + }; + + // 渲染单个简化项目 + const renderSimpleItem = (item: SimpleMarkShow) => { + return ( +
+
+
+
+ +

{item.id}

+
+
+ +

{item.title || '-'}

+
+
+ +
+ +

{item.description || '-'}

+
+ +
+
+ +
{renderTags(item.tags)}
+
+
+ +

{item.summary || '-'}

+
+
+ +
+
+ +
+ {item.cover ? ( + 封面 { + (e.target as HTMLImageElement).style.display = 'none'; + }} + /> + ) : ( + - + )} +
+
+
+ +
+ {item.link ? ( + + {item.link} + + ) : ( + - + )} +
+
+
+
+
+ ); + }; + + // 渲染单个完整项目 + const renderFullItem = (item: MarkShow) => { + return ( +
+
+
+
+ +

{item.id}

+
+
+ +

{item.title || '-'}

+
+
+ +
{renderMarkType(item.markType)}
+
+
+ +
+ +

{item.description || '-'}

+
+ +
+
+ +
{renderTags(item.tags)}
+
+
+ +

{item.summary || '-'}

+
+
+ +
+
+ +

{item.key || '-'}

+
+
+ +

{formatDate(item.createdAt)}

+
+
+ +

{formatDate(item.updatedAt)}

+
+
+ +
+
+ +

{formatDate(item.markedAt)}

+
+
+ +
+ {item.cover ? ( + 封面 { + (e.target as HTMLImageElement).style.display = 'none'; + }} + /> + ) : ( + - + )} +
+
+
+ +
+ +
+ {item.link ? ( + + {item.link} + + ) : ( + - + )} +
+
+ +
+ +
+
+                {JSON.stringify(item.data, null, 2)}
+              
+
+
+
+
+ ); + }; + + // 行渲染函数 + const rowRenderer = ({ index, key, style }: any) => { + const item = displayData[index]; + + return ( +
+
+ {showAll ? renderFullItem(item as MarkShow) : renderSimpleItem(item as SimpleMarkShow)} +
+
+ ); + }; + + // 格式化日期显示 + const formatDate = (date: string | Date | undefined) => { + if (!date) return '-'; + return new Date(date).toLocaleString('zh-CN'); + }; + + // 渲染标签 + const renderTags = (tags?: string[]) => { + if (!tags || tags.length === 0) return '-'; + return ( +
+ {tags.map((tag, index) => ( + + {tag} + + ))} +
+ ); + }; + + // 渲染类型徽章 + const renderMarkType = (markType?: string) => { + if (!markType) return '-'; + + const typeColors: Record = { + markdown: 'bg-green-100 text-green-800', + json: 'bg-yellow-100 text-yellow-800', + html: 'bg-orange-100 text-orange-800', + image: 'bg-purple-100 text-purple-800', + video: 'bg-red-100 text-red-800', + audio: 'bg-pink-100 text-pink-800', + code: 'bg-gray-100 text-gray-800', + link: 'bg-blue-100 text-blue-800', + file: 'bg-indigo-100 text-indigo-800', + }; + + const colorClass = typeColors[markType] || 'bg-gray-100 text-gray-800'; + + return ( + + {markType} + + ); + }; + + // 渲染虚拟滚动列表 + const renderVirtualizedList = () => { + return ( + + {({ height, width }) => ( + + )} + + ); + }; + + return ( +
+ {/* 头部控制区域 */} +
+
+

+ SimpleMark 信息显示 +

+
+ + 显示模式: + + +
+ 共 {data.length} 条记录 +
+
+
+
+ + {/* 数据显示区域 */} +
+ {data.length === 0 ? ( +
+
+
📝
+

暂无数据

+
+
+ ) : ( + renderVirtualizedList() + )} +
+
+ ); +}; diff --git a/web/src/apps/muse/base/docs/README.md b/web/src/apps/muse/base/docs/README.md new file mode 100644 index 0000000..576e72b --- /dev/null +++ b/web/src/apps/muse/base/docs/README.md @@ -0,0 +1,191 @@ +# 文档组件 (DocsComponent) + +一个现代化的左侧导航右侧内容的文档显示组件,支持多种内容类型和优雅的样式设计。 + +## 特性 + +- 🎨 **现代化设计**: 清新的UI设计,优雅的色彩搭配 +- 📱 **响应式布局**: 支持桌面和移动端适配 +- 📝 **Markdown支持**: 使用 marked 库,支持 GitHub Flavored Markdown +- 🔄 **多内容类型**: 支持Markdown、代码、JSON、图片等多种类型 +- ⚡ **流畅交互**: 带加载状态和平滑过渡动画 +- 🏷️ **标签系统**: 支持文档标签和分类 +- 🔍 **清晰导航**: 左侧树形导航,快速定位文档 +- ✨ **丰富语法**: 支持表格、任务列表、代码高亮等 GFM 特性 + +## 使用方法 + +### 基础用法 + +```tsx +import React from 'react'; +import { DocsComponent } from './docs'; +import { mockMarks } from './mock/collection'; + +function App() { + return ( +
+ +
+ ); +} +``` + +### 自定义数据 + +```tsx +import { DocsComponent, Mark } from './docs'; + +const customDocs: Mark[] = [ + { + id: '1', + title: '快速开始', + description: '了解如何快速开始使用我们的产品', + tags: ['入门', '指南'], + markType: 'markdown', + data: { + content: `# 快速开始 + +这里是文档内容...` + }, + createdAt: new Date(), + updatedAt: new Date() + } +]; + +function App() { + return ; +} +``` + +## 数据结构 + +组件接受一个 `Mark[]` 类型的数据源,每个Mark对象包含: + +```typescript +type Mark = { + id: string; // 唯一标识 + title?: string; // 文档标题 + description?: string; // 文档描述 + tags?: string[]; // 标签数组 + markType?: string; // 内容类型 + data: any; // 内容数据 + createdAt: Date; // 创建时间 + updatedAt: Date; // 更新时间 + // ... 其他字段 +} +``` + +## 支持的内容类型 + +### Markdown +```typescript +{ + markType: 'markdown', + data: { + content: '# 标题\n\n这是Markdown内容...' + } +} +``` + +### 代码 +```typescript +{ + markType: 'code', + data: { + code: 'const hello = "world";', + language: 'javascript' + } +} +``` + +### JSON数据 +```typescript +{ + markType: 'json', + data: { + // 任何JSON数据 + } +} +``` + +### 图片 +```typescript +{ + markType: 'image', + data: { + src: 'https://example.com/image.jpg', + alt: '图片描述' + } +} +``` + +## 样式定制 + +组件使用CSS类名,你可以通过覆盖这些类名来定制样式: + +```css +/* 主容器 */ +.docs-container { } + +/* 导航区域 */ +.docs-nav { } +.docs-nav-item { } +.docs-nav-link { } + +/* 内容区域 */ +.docs-content { } +.docs-content-header { } +.docs-content-body { } + +/* Markdown内容 */ +.docs-markdown { } +``` + +## 组件API + +### Props + +| 属性 | 类型 | 默认值 | 描述 | +|------|------|--------|------| +| dataSource | Mark[] | [] | 文档数据源 | + +### 导出组件 + +- `DocsComponent`: 主要的文档组件 +- `App`: DocsComponent的别名,保持向后兼容 + +## 示例 + +查看 `example.tsx` 文件获取完整的使用示例。 + +## 开发 + +```bash +# 安装依赖 +npm install marked +# 或 +pnpm install marked +``` + +组件使用 `marked` 库进行 Markdown 渲染,支持: + +- ✅ GitHub Flavored Markdown (GFM) +- ✅ 表格语法 +- ✅ 任务列表 +- ✅ 代码块语法高亮 +- ✅ 自动链接识别 +- ✅ 删除线语法 + +## 注意事项 + +1. 确保容器有足够的高度(建议设置为 `100vh`) +2. 组件会自动选中第一个文档项 +3. 支持键盘导航和无障碍访问 +4. 在移动端会自动调整为上下布局 + +## 更新日志 + +- v1.0.0: 初始版本,支持基础的文档显示功能 +- 支持Markdown、代码、JSON、图片等内容类型 +- 响应式设计和现代化UI \ No newline at end of file diff --git a/web/src/apps/muse/base/docs/docs.css b/web/src/apps/muse/base/docs/docs.css new file mode 100644 index 0000000..1b2fe6a --- /dev/null +++ b/web/src/apps/muse/base/docs/docs.css @@ -0,0 +1,542 @@ +/* 文档组件样式 */ +.docs-container { + display: flex; + height: 100%; + background: #f8fafc; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +/* 左侧导航区域 */ +.docs-nav { + width: 280px; + background: #ffffff; + border-right: 1px solid #e2e8f0; + flex-shrink: 0; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.docs-nav-header { + padding: 20px; + border-bottom: 1px solid #e2e8f0; + background: #f8fafc; + position: sticky; + top: 0; + z-index: 10; + flex-shrink: 0; +} + +.docs-nav-title { + font-size: 18px; + font-weight: 600; + color: #1e293b; + margin: 0; +} + +.docs-nav-list { + list-style: none; + padding: 0; + margin: 0; + flex: 1; + overflow-y: auto; +} + +.docs-nav-item { + border-bottom: 1px solid #f1f5f9; + transition: all 0.2s ease; +} + +.docs-nav-item:last-child { + border-bottom: none; +} + +.docs-nav-link { + display: block; + padding: 16px 20px; + color: #64748b; + text-decoration: none; + transition: all 0.2s ease; + cursor: pointer; + border-left: 3px solid transparent; +} + +.docs-nav-link:hover { + background: #f8fafc; + color: #334155; + border-left-color: #e2e8f0; +} + +.docs-nav-link.active { + background: #eff6ff; + color: #2563eb; + border-left-color: #2563eb; + font-weight: 500; +} + +.docs-nav-link-title { + font-size: 14px; + font-weight: 500; + margin-bottom: 4px; + line-height: 1.4; +} + +.docs-nav-link-desc { + font-size: 12px; + color: #94a3b8; + line-height: 1.3; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; +} + +.docs-nav-link.active .docs-nav-link-desc { + color: #60a5fa; +} + +/* 右侧内容区域 */ +.docs-content { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.docs-content-header { + padding: 20px 32px; + background: #ffffff; + border-bottom: 1px solid #e2e8f0; + flex-shrink: 0; +} + +.docs-content-title { + font-size: 24px; + font-weight: 700; + color: #1e293b; + margin: 0 0 8px 0; + line-height: 1.3; +} + +.docs-content-meta { + display: flex; + gap: 16px; + align-items: center; + margin-top: 8px; +} + +.docs-content-tag { + display: inline-flex; + align-items: center; + padding: 4px 8px; + background: #f1f5f9; + color: #475569; + border-radius: 6px; + font-size: 12px; + font-weight: 500; +} + +.docs-content-date { + font-size: 12px; + color: #94a3b8; +} + +.docs-content-body { + flex: 1; + padding: 32px; + overflow-y: auto; + background: #ffffff; +} + +.docs-content-body.empty { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + color: #94a3b8; +} + +.docs-empty-icon { + font-size: 48px; + margin-bottom: 16px; + opacity: 0.5; +} + +.docs-empty-text { + font-size: 16px; + text-align: center; + line-height: 1.5; +} + +/* Markdown内容样式 */ +.docs-markdown { + max-width: none; + color: #374151; + line-height: 1.7; +} + +.docs-markdown h1, +.docs-markdown h2, +.docs-markdown h3, +.docs-markdown h4, +.docs-markdown h5, +.docs-markdown h6 { + color: #111827; + font-weight: 600; + margin: 24px 0 16px 0; + line-height: 1.25; +} + +.docs-markdown h1 { + font-size: 32px; + border-bottom: 1px solid #e5e7eb; + padding-bottom: 12px; +} + +.docs-markdown h2 { + font-size: 24px; + border-bottom: 1px solid #f3f4f6; + padding-bottom: 8px; +} + +.docs-markdown h3 { + font-size: 20px; +} + +.docs-markdown h4 { + font-size: 16px; +} + +.docs-markdown p { + margin: 16px 0; + line-height: 1.7; +} + +.docs-markdown ul, +.docs-markdown ol { + margin: 16px 0; + padding-left: 24px; +} + +.docs-markdown li { + margin: 8px 0; + line-height: 1.6; +} + +.docs-markdown blockquote { + margin: 16px 0; + padding: 16px 20px; + background: #f9fafb; + border-left: 4px solid #d1d5db; + color: #6b7280; + font-style: italic; +} + +.docs-markdown code { + background: #f3f4f6; + color: #e11d48; + padding: 2px 6px; + border-radius: 4px; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 0.875em; +} + +.docs-markdown pre { + background: #1f2937; + color: #f9fafb; + padding: 20px; + border-radius: 8px; + overflow-x: auto; + margin: 16px 0; +} + +.docs-markdown pre code { + background: none; + color: inherit; + padding: 0; + border-radius: 0; +} + +.docs-markdown a { + color: #2563eb; + text-decoration: none; +} + +.docs-markdown a:hover { + text-decoration: underline; +} + +.docs-markdown img { + max-width: 100%; + height: auto; + border-radius: 8px; + margin: 16px 0; +} + +.docs-markdown table { + width: 100%; + border-collapse: collapse; + margin: 16px 0; +} + +.docs-markdown th, +.docs-markdown td { + border: 1px solid #e5e7eb; + padding: 12px; + text-align: left; +} + +.docs-markdown th { + background: #f9fafb; + font-weight: 600; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .docs-container { + flex-direction: column; + height: auto; + } + + .docs-nav { + width: 100%; + max-height: 300px; + display: flex; + flex-direction: column; + } + + .docs-nav-header { + position: sticky; + top: 0; + z-index: 10; + } + + .docs-nav-list { + flex: 1; + overflow-y: auto; + } + + .docs-content-header { + padding: 16px 20px; + } + + .docs-content-body { + padding: 20px; + } + + .docs-content-title { + font-size: 20px; + } +} + +/* 滚动条样式 */ +.docs-nav-list::-webkit-scrollbar, +.docs-content-body::-webkit-scrollbar { + width: 6px; +} + +.docs-nav-list::-webkit-scrollbar-track, +.docs-content-body::-webkit-scrollbar-track { + background: #f1f5f9; +} + +.docs-nav-list::-webkit-scrollbar-thumb, +.docs-content-body::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 3px; +} + +.docs-nav-list::-webkit-scrollbar-thumb:hover, +.docs-content-body::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* 不同内容类型的样式 */ +.docs-json-content { + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 20px; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 14px; + line-height: 1.5; + color: #374151; + overflow-x: auto; +} + +.docs-code-content { + background: #1e293b; + color: #f1f5f9; + border-radius: 8px; + padding: 20px; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 14px; + line-height: 1.6; + overflow-x: auto; + margin: 16px 0; +} + +.docs-code-content code { + background: none; + color: inherit; + padding: 0; + border-radius: 0; +} + +.docs-image-content { + text-align: center; + margin: 20px 0; +} + +.docs-image-content img { + max-width: 100%; + height: auto; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +.docs-default-content { + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 8px; + padding: 20px; +} + +.docs-default-content pre { + background: none; + color: #374151; + padding: 0; + margin: 0; + font-size: 14px; + line-height: 1.5; +} + +/* 内容区域优化 */ +.docs-content-body .docs-markdown h1:first-child { + margin-top: 0; +} + +.docs-content-body .docs-markdown h1:last-child, +.docs-content-body .docs-markdown h2:last-child, +.docs-content-body .docs-markdown h3:last-child, +.docs-content-body .docs-markdown p:last-child { + margin-bottom: 0; +} + +/* 导航项类型标识 */ +.docs-nav-item::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 0; + background: #2563eb; + transition: height 0.2s ease; +} + +.docs-nav-item { + position: relative; +} + +.docs-nav-link.active ~ ::before, +.docs-nav-item:has(.docs-nav-link.active)::before { + height: 100%; +} + +/* 标签优化 */ +.docs-content-tag.type-markdown { + background: #dbeafe; + color: #1e40af; +} + +.docs-content-tag.type-code { + background: #f3e8ff; + color: #7c3aed; +} + +.docs-content-tag.type-json { + background: #ecfdf5; + color: #059669; +} + +.docs-content-tag.type-image { + background: #fef3c7; + color: #d97706; +} + +/* 加载状态 */ +.docs-loading { + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + color: #94a3b8; +} + +.docs-loading-spinner { + width: 20px; + height: 20px; + border: 2px solid #e2e8f0; + border-top: 2px solid #2563eb; + border-radius: 50%; + animation: docs-spin 1s linear infinite; + margin-right: 12px; +} + +@keyframes docs-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 搜索和过滤功能样式 */ +.docs-nav-search { + padding: 16px 20px; + border-bottom: 1px solid #e2e8f0; + background: #ffffff; + position: sticky; + top: 0; + z-index: 10; + flex-shrink: 0; +} + +.docs-nav-search-input { + width: 100%; + padding: 8px 12px; + border: 1px solid #d1d5db; + border-radius: 6px; + font-size: 14px; + transition: border-color 0.2s ease; +} + +.docs-nav-search-input:focus { + outline: none; + border-color: #2563eb; + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); +} + +.docs-nav-search-input::placeholder { + color: #9ca3af; +} + +/* 内容区域的打印样式 */ +@media print { + .docs-nav { + display: none; + } + + .docs-content { + width: 100%; + } + + .docs-content-body { + padding: 0; + } + + .docs-markdown { + color: #000; + } +} \ No newline at end of file diff --git a/web/src/apps/muse/base/docs/example.tsx b/web/src/apps/muse/base/docs/example.tsx new file mode 100644 index 0000000..1ca26c8 --- /dev/null +++ b/web/src/apps/muse/base/docs/example.tsx @@ -0,0 +1,222 @@ +import React from 'react'; +import { DocsComponent } from './index'; +import { mockMarks, generateMarkWithType } from '../mock/collection'; + +// 创建一些示例文档数据 +const createSampleDocs = () => { + const markdownDoc = generateMarkWithType('markdown'); + markdownDoc.title = '项目介绍'; + markdownDoc.description = '了解我们的项目背景和目标'; + markdownDoc.tags = ['介绍', '项目']; + markdownDoc.data = { + content: `# 欢迎使用文档系统 + +这是一个现代化的文档展示系统,现在使用 **marked** 库进行 Markdown 渲染。 + +## 主要功能 + +- **响应式设计**: 适配各种屏幕尺寸 +- **左侧导航**: 清晰的文档结构 +- **Markdown支持**: 使用 marked 库,支持 GitHub Flavored Markdown +- **多种内容类型**: 支持文本、代码、图片等多种内容 + +## 技术特性 + +### 样式设计 +使用了现代化的CSS设计,包括: + +1. 清新的配色方案 +2. 优雅的阴影和边框 +3. 流畅的过渡动画 + +### 功能特性 + +> **提示**: 这是一个引用块,展示了 marked 的渲染能力 + +- [x] 点击导航自动切换内容 +- [x] 加载状态提示 +- [x] 标签和日期显示 +- [ ] 响应式布局 + +## 代码示例 + +### TypeScript 代码 + +\`\`\`typescript +import { DocsComponent } from './docs'; +import { mockMarks } from './mock/collection'; + +const App: React.FC = () => { + return ; +}; +\`\`\` + +### 内联代码 + +使用 \`marked\` 库可以更好地处理 Markdown 语法。 + +## 表格支持 + +| 功能 | 状态 | 描述 | +|------|------|------| +| Markdown | ✅ | 完整支持 | +| 代码高亮 | ✅ | 语法高亮 | +| 表格 | ✅ | GFM 表格 | +| 任务列表 | ✅ | 支持复选框 | + +## 链接和图片 + +- 外部链接: [GitHub](https://github.com) +- 图片支持: ![示例](https://via.placeholder.com/150) + +--- + +希望你喜欢这个使用 **marked** 的文档系统!🎉` + }; + + const apiDoc = generateMarkWithType('markdown'); + apiDoc.title = 'API 文档'; + apiDoc.description = 'API接口使用说明和示例'; + apiDoc.tags = ['API', '开发']; + apiDoc.data = { + content: `# API 文档 + +## 🔐 用户认证 + +### 登录接口 + +**请求地址**: \`POST /api/auth/login\` + +**请求参数**: + +| 参数 | 类型 | 必填 | 描述 | +|------|------|------|------| +| username | string | ✅ | 用户名 | +| password | string | ✅ | 密码 | + +**响应示例**: + +\`\`\`json +{ + "code": 200, + "message": "success", + "data": { + "token": "eyJhbGciOiJIUzI1NiIs...", + "user": { + "id": 1, + "username": "admin", + "email": "admin@example.com" + } + } +} +\`\`\` + +## 📊 数据操作 + +### 获取列表 + +**请求地址**: \`GET /api/data/list\` + +**查询参数**: + +- \`page\`: 页码 (默认: 1) +- \`size\`: 每页数量 (默认: 10) +- \`keyword\`: 搜索关键词 + +### 创建数据 + +**请求地址**: \`POST /api/data/create\` + +**请求体**: + +\`\`\`json +{ + "title": "标题", + "content": "内容", + "tags": ["tag1", "tag2"] +} +\`\`\` + +## ⚠️ 错误码 + +| 错误码 | 描述 | 解决方案 | +|--------|------|----------| +| 400 | 请求参数错误 | 检查请求参数格式 | +| 401 | 未授权 | 重新登录获取 token | +| 403 | 禁止访问 | 检查用户权限 | +| 500 | 服务器错误 | 联系技术支持 | + +> **注意**: 所有 API 请求都需要在 Header 中包含 \`Authorization: Bearer \` + +更多 API 详情请参考 [完整文档](https://docs.example.com) 📖` + }; + + const codeDoc = generateMarkWithType('code'); + codeDoc.title = '代码示例'; + codeDoc.description = '常用的代码片段和最佳实践'; + codeDoc.tags = ['代码', '示例']; + codeDoc.data = { + code: `// React Hook 示例 +import { useState, useEffect, useCallback } from 'react'; + +const useDocuments = (initialData = []) => { + const [docs, setDocs] = useState(initialData); + const [loading, setLoading] = useState(false); + const [selectedId, setSelectedId] = useState(null); + + // 获取文档列表 + const fetchDocs = useCallback(async () => { + setLoading(true); + try { + const response = await fetch('/api/docs'); + const data = await response.json(); + setDocs(data); + } catch (error) { + console.error('Failed to fetch docs:', error); + } finally { + setLoading(false); + } + }, []); + + // 选择文档 + const selectDoc = useCallback((id) => { + setSelectedId(id); + }, []); + + useEffect(() => { + fetchDocs(); + }, [fetchDocs]); + + return { + docs, + loading, + selectedId, + selectDoc, + refetch: fetchDocs + }; +}; + +export default useDocuments;`, + language: 'typescript' + }; + + const configDoc = generateMarkWithType('json'); + configDoc.title = '配置说明'; + configDoc.description = '系统配置项说明和默认值'; + configDoc.tags = ['配置', '设置']; + + return [markdownDoc, apiDoc, codeDoc, configDoc]; +}; + +// 示例组件 +export const DocsExample: React.FC = () => { + const sampleDocs = createSampleDocs(); + + return ( +
+ +
+ ); +}; + +export default DocsExample; \ No newline at end of file diff --git a/web/src/apps/muse/base/docs/index.tsx b/web/src/apps/muse/base/docs/index.tsx new file mode 100644 index 0000000..08c8a74 --- /dev/null +++ b/web/src/apps/muse/base/docs/index.tsx @@ -0,0 +1,222 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { marked } from 'marked'; +import dayjs from 'dayjs'; +import { Mark } from '../mock/collection'; +import './docs.css'; + +type Props = { + dataSource?: Mark[]; +} + +// 配置 marked 选项 +marked.setOptions({ + breaks: true, + gfm: true, +}); + +// Markdown渲染组件 +const MarkdownRenderer: React.FC<{ content: string }> = ({ content }) => { + const [htmlContent, setHtmlContent] = useState(''); + + useEffect(() => { + const renderMarkdown = async () => { + try { + const html = await marked(content); + setHtmlContent(html); + } catch (error) { + console.error('Markdown rendering error:', error); + const errorMessage = error instanceof Error ? error.message : '未知错误'; + setHtmlContent(`

渲染错误: ${errorMessage}

`); + } + }; + + renderMarkdown(); + }, [content]); + + return ( +
+ ); +}; + +// 内容渲染组件 +const ContentRenderer: React.FC<{ mark: Mark }> = ({ mark }) => { + const renderContent = () => { + if (!mark.data) { + return
暂无内容
; + } + if (mark.description) { + return ; + } + + // 根据markType渲染不同类型的内容 + switch (mark.markType) { + case 'markdown': + if (mark.data.content) { + return ; + } + break; + case 'json': + return ( +
+            {JSON.stringify(mark.data, null, 2)}
+          
+ ); + case 'code': + return ( +
+            {mark.data.code || JSON.stringify(mark.data, null, 2)}
+          
+ ); + case 'image': + if (mark.data.src) { + return ( +
+ {mark.data.alt +
+ ); + } + break; + default: + // 对于其他类型,尝试显示内容字段 + if (mark.data.content) { + return ; + } + // 如果没有内容字段,显示JSON格式 + return ( +
+
{JSON.stringify(mark.data, null, 2)}
+
+ ); + } + + return
无法显示此类型的内容
; + }; + + return <>{renderContent()}; +}; + +// 主要的Docs组件 +export const DocsComponent: React.FC = ({ dataSource = [] }) => { + const [selectedMarkId, setSelectedMarkId] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + // 过滤和处理数据源 + const validMarks = useMemo(() => { + return dataSource.filter(mark => mark.title || mark.description); + }, [dataSource]); + + // 获取当前选中的Mark + const selectedMark = useMemo(() => { + return validMarks.find(mark => mark.id === selectedMarkId) || null; + }, [validMarks, selectedMarkId]); + + // 默认选中第一项 + useEffect(() => { + if (validMarks.length > 0 && !selectedMarkId) { + setSelectedMarkId(validMarks[0].id); + } + }, [validMarks, selectedMarkId]); + + // 处理导航项点击 + const handleNavItemClick = (markId: string) => { + if (markId !== selectedMarkId) { + setIsLoading(true); + setSelectedMarkId(markId); + + // 模拟加载时间 + setTimeout(() => { + setIsLoading(false); + }, 200); + } + }; + + // 格式化日期 + const formatDate = (date: Date) => { + return dayjs(date).format('YYYY年MM月DD日'); + }; + + return ( +
+ {/* 左侧导航 */} + + + {/* 右侧内容 */} +
+ {selectedMark ? ( + <> + {/* 内容标题栏 */} +
+

+ {selectedMark.title || '未命名文档'} +

+
+ {selectedMark.tags && selectedMark.tags.map((tag, index) => ( + + {tag} + + ))} + + {formatDate(selectedMark.updatedAt)} + +
+
+ + {/* 内容主体 */} +
+ {isLoading ? ( +
+
+ 加载中... +
+ ) : ( + + )} +
+ + ) : ( +
+
📄
+
+ {validMarks.length === 0 ? '暂无文档可显示' : '请选择一个文档查看'} +
+
+ )} +
+
+ ); +}; + +// 兼容性导出 +export const App = DocsComponent; \ No newline at end of file diff --git a/web/src/apps/muse/base/graph/sigma/index.tsx b/web/src/apps/muse/base/graph/sigma/index.tsx new file mode 100644 index 0000000..9888db6 --- /dev/null +++ b/web/src/apps/muse/base/graph/sigma/index.tsx @@ -0,0 +1,127 @@ +import React, { useEffect, useRef, useState } from 'react'; +import Graph from 'graphology'; +import Sigma from 'sigma'; +import { Mark } from '../../../modules/mark'; + + +type Props = { + dataSource?: Mark[]; +} + +export const SigmaGraph = (props: Props) => { + const { dataSource = [] } = props; + const containerRef = useRef(null); + const sigmaRef = useRef(null); + const graphRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + + // 创建图实例 + const graph = new Graph(); + graphRef.current = graph; + + // 创建 Sigma 实例 + const sigma = new Sigma(graph, containerRef.current, { + renderLabels: true, + labelRenderedSizeThreshold: 8, + labelDensity: 8, + }); + sigmaRef.current = sigma; + + return () => { + sigma.kill(); + }; + }, []); + + useEffect(() => { + if (!graphRef.current || !dataSource.length) return; + + const graph = graphRef.current; + + // 清空现有图数据 + graph.clear(); + + // 添加节点 + dataSource.forEach((mark, index) => { + graph.addNode(`node-${index}`, { + x: Math.random() * 800, + y: Math.random() * 600, + size: Math.random() * 20 + 5, + label: mark.title || `Mark ${index}`, + color: `#${Math.floor(Math.random() * 16777215).toString(16)}`, + }); + }); + + // // 添加一些随机边(基于数据关系或随机生成) + // for (let i = 0; i < Math.min(dataSource.length - 1, 20); i++) { + // const sourceIndex = Math.floor(Math.random() * dataSource.length); + // const targetIndex = Math.floor(Math.random() * dataSource.length); + + // if (sourceIndex !== targetIndex) { + // try { + // graph.addEdge(`node-${sourceIndex}`, `node-${targetIndex}`, { + // size: Math.random() * 5 + 1, + // color: '#999', + // }); + // } catch (error) { + // // 边已存在,忽略错误 + // } + // } + // } + + // 刷新 Sigma 渲染 + if (sigmaRef.current) { + sigmaRef.current.refresh(); + } + }, [dataSource]); + + // 添加交互事件处理 + useEffect(() => { + if (!sigmaRef.current) return; + + const sigma = sigmaRef.current; + + // 节点点击事件 + const handleNodeClick = (event: any) => { + console.log('Node clicked:', event.node); + }; + + // 节点悬停事件 + const handleNodeHover = (event: any) => { + console.log('Node hovered:', event.node); + }; + + sigma.on('clickNode', handleNodeClick); + sigma.on('enterNode', handleNodeHover); + + return () => { + sigma.off('clickNode', handleNodeClick); + sigma.off('enterNode', handleNodeHover); + }; + }, []); + + return ( +
+
+
+

节点数量: {dataSource.length}

+

使用鼠标拖拽和缩放来探索图形

+
+
+ ); +}; \ No newline at end of file diff --git a/web/src/apps/muse/base/index.tsx b/web/src/apps/muse/base/index.tsx new file mode 100644 index 0000000..6ad92d2 --- /dev/null +++ b/web/src/apps/muse/base/index.tsx @@ -0,0 +1,92 @@ +import { useEffect, useState } from "react"; +import { Base } from "./table/index"; +import { markService } from "../modules/mark-service"; +import { SigmaGraph } from "./graph/sigma/index"; +import { DocsComponent } from "./docs"; +import { MarkDetailList } from "./card/MarkDetailList"; + +const tabs = [ + { + key: 'table', + title: '表格' + }, + { + key: 'card', + title: '卡片' + }, + { + key: 'graph', + title: '关系图' + }, + { + key: 'docs', + title: '文档' + }, + { + key: 'world', + title: '世界' + } +]; + +export const BaseApp = () => { + const [activeTab, setActiveTab] = useState('table'); + const [dataSource, setDataSource] = useState([]); + useEffect(() => { + getMarks(); + }, []); + const getMarks = async () => { + const marks = await markService.getAllMarks(); + setDataSource(marks); + } + const renderContent = () => { + switch (activeTab) { + case 'table': + return ; + case 'graph': + return ( +
+ +
+ ); + case 'card': + return ; + case 'docs': + return ; + case 'world': + return ( +
+ 世界模块暂未实现 +
+ ); + default: + return null; + } + }; + + return ( +
+ {/* Tab 导航栏 */} +
+ +
+ + {/* Tab 内容区域 */} +
+ {renderContent()} +
+
+ ); +} \ No newline at end of file diff --git a/web/src/apps/muse/base/mock/collection.ts b/web/src/apps/muse/base/mock/collection.ts new file mode 100644 index 0000000..9bacc93 --- /dev/null +++ b/web/src/apps/muse/base/mock/collection.ts @@ -0,0 +1,326 @@ +import { faker } from '@faker-js/faker'; +import { nanoid, customAlphabet } from 'nanoid'; + +export const random = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); + +// 确保类型定义 +const ensureType = ['markdown', 'json', 'html', 'image', 'video', 'audio', 'code', 'link', 'file']; +export type MarkEnsureType = typeof ensureType[number]; + +// 根据新的类型定义 +export type Mark = { + /** + * 标记ID + */ + id: string; + + /** + * 标题 + */ + title?: string; + + /** + * 描述 + */ + description?: string; + + /** + * 标签 + */ + tags?: string[]; + + /** + * 标记类型 + */ + markType?: string; + + /** + * 封面 + */ + cover?: string; + + /** + * 链接 + */ + link?: string; + + /** + * 摘要 + */ + summary?: string; + + /** + * 键 + */ + key?: string; + data: T; + + /** + * 附件列表 + */ + fileList?: any[]; + /** + * 创建人信息 + */ + uname?: string; + /** + * 版本号 + */ + version?: number; + /** + * 创建时间 + */ + createdAt: Date; + /** + * 更新时间 + */ + updatedAt: Date; + /** + * 标记时间 + */ + markedAt?: Date; + uid?: string; + puid?: string; +}; + +// 保留原有的辅助类型 +export type MarkDataNode = { + id?: string; + content?: string; + type?: string; + title?: string; + position?: { x: number; y: number }; + size?: { width: number; height: number }; + metadata?: Record; + [key: string]: any; +}; + +export type MarkFile = { + id: string; + name: string; + url: string; + size: number; + type: 'self' | 'data' | 'generate'; + query: string; + hash: string; + fileKey: string; +}; + +export type MarkData = { + md?: string; + mdList?: string[]; + type?: MarkEnsureType; + data?: any; + key?: string; + push?: boolean; + pushTime?: Date; + summary?: string; + nodes?: MarkDataNode[]; + [key: string]: any; +}; + +// 生成模拟的 MarkDataNode +const generateMarkDataNode = (): MarkDataNode => ({ + id: random(12), + content: faker.lorem.paragraph(), + type: faker.helpers.arrayElement(['text', 'image', 'video', 'code', 'link']), + title: faker.lorem.sentence(), + position: { + x: faker.number.int({ min: 0, max: 1920 }), + y: faker.number.int({ min: 0, max: 1080 }) + }, + size: { + width: faker.number.int({ min: 100, max: 800 }), + height: faker.number.int({ min: 50, max: 600 }) + }, + metadata: { + createdBy: faker.person.fullName(), + lastModified: faker.date.recent(), + isLocked: faker.datatype.boolean() + } +}); + +// 生成模拟的附件 +const generateFileAttachment = (): any => ({ + id: faker.string.uuid(), + name: faker.system.fileName(), + url: faker.internet.url(), + size: faker.number.int({ min: 1024, max: 50 * 1024 * 1024 }), + type: faker.helpers.arrayElement(['image', 'document', 'video', 'audio']), + mimeType: faker.system.mimeType(), + hash: faker.git.commitSha(), + uploadedAt: faker.date.recent() +}); + +// 生成模拟的 MarkData(通用数据类型) +const generateMarkData = (): T => { + const dataVariants = [ + // Markdown 数据 + { + content: faker.lorem.paragraphs(3, '\n\n'), + format: 'markdown', + wordCount: faker.number.int({ min: 100, max: 1000 }) + }, + // JSON 数据 + { + jsonData: { + name: faker.person.fullName(), + email: faker.internet.email(), + settings: { + theme: faker.helpers.arrayElement(['light', 'dark']), + language: faker.helpers.arrayElement(['zh-CN', 'en-US', 'ja-JP']) + } + }, + schema: 'user-profile' + }, + // 图片数据 + { + src: faker.image.url(), + alt: faker.lorem.sentence(), + width: faker.number.int({ min: 400, max: 1920 }), + height: faker.number.int({ min: 300, max: 1080 }), + format: faker.helpers.arrayElement(['jpg', 'png', 'webp']) + }, + // 代码数据 + { + code: `function ${faker.hacker.noun()}() {\n return "${faker.hacker.phrase()}";\n}`, + language: faker.helpers.arrayElement(['javascript', 'typescript', 'python', 'java']), + lineCount: faker.number.int({ min: 10, max: 100 }) + }, + // 链接数据 + { + url: faker.internet.url(), + title: faker.lorem.sentence(), + description: faker.lorem.paragraph(), + favicon: faker.image.url({ width: 32, height: 32 }) + } + ]; + + return faker.helpers.arrayElement(dataVariants) as T; +}; + +// 生成单个 Mark 记录 +const generateMark = (): Mark => { + const createdAt = faker.date.past(); + const updatedAt = faker.date.between({ from: createdAt, to: new Date() }); + + return { + id: faker.string.uuid(), + title: faker.datatype.boolean() ? faker.lorem.sentence({ min: 3, max: 8 }) : undefined, + description: faker.datatype.boolean() ? faker.lorem.paragraph() : undefined, + tags: faker.datatype.boolean() + ? Array.from({ length: faker.number.int({ min: 1, max: 5 }) }, () => + faker.helpers.arrayElement(['技术', '前端', '后端', '设计', 'AI', '工具', '教程', '笔记', '生活', '工作']) + ) + : undefined, + markType: faker.datatype.boolean() + ? faker.helpers.arrayElement(ensureType) + : undefined, + cover: faker.datatype.boolean() + ? faker.image.url({ width: 800, height: 600 }) + : undefined, + link: faker.datatype.boolean() + ? faker.internet.url() + : undefined, + summary: faker.datatype.boolean() + ? faker.lorem.sentence() + : undefined, + key: faker.datatype.boolean() + ? faker.system.filePath() + : undefined, + data: generateMarkData(), + fileList: faker.datatype.boolean() + ? Array.from({ length: faker.number.int({ min: 1, max: 4 }) }, generateFileAttachment) + : undefined, + uname: faker.datatype.boolean() + ? faker.person.fullName() + : undefined, + version: faker.datatype.boolean() + ? faker.number.int({ min: 1, max: 10 }) + : undefined, + createdAt, + updatedAt, + markedAt: faker.datatype.boolean() + ? faker.date.between({ from: createdAt, to: updatedAt }) + : undefined, + uid: faker.datatype.boolean() + ? faker.string.uuid() + : undefined, + puid: faker.datatype.boolean() + ? faker.string.uuid() + : undefined + }; +}; + +// 生成指定类型的 Mark 记录 +const generateMarkWithType = (markType: MarkEnsureType): Mark => { + const mark = generateMark(); + mark.markType = markType; + + // 根据类型生成相应的数据 + switch (markType) { + case 'markdown': + mark.data = { + content: faker.lorem.paragraphs(faker.number.int({ min: 2, max: 5 }), '\n\n'), + format: 'markdown', + wordCount: faker.number.int({ min: 100, max: 1000 }) + }; + break; + case 'json': + mark.data = { + jsonData: { + id: faker.string.uuid(), + name: faker.person.fullName(), + settings: { + theme: faker.helpers.arrayElement(['light', 'dark']), + notifications: faker.datatype.boolean() + } + } + }; + break; + case 'image': + mark.data = { + src: faker.image.url(), + alt: faker.lorem.sentence(), + width: faker.number.int({ min: 400, max: 1920 }), + height: faker.number.int({ min: 300, max: 1080 }) + }; + break; + case 'video': + mark.data = { + src: faker.internet.url() + '/video.mp4', + duration: faker.number.int({ min: 30, max: 3600 }), + resolution: faker.helpers.arrayElement(['720p', '1080p', '4K']) + }; + break; + case 'code': + mark.data = { + code: `function ${faker.hacker.noun()}() {\n return "${faker.hacker.phrase()}";\n}`, + language: faker.helpers.arrayElement(['javascript', 'typescript', 'python', 'java']), + lineCount: faker.number.int({ min: 10, max: 100 }) + }; + break; + default: + mark.data = generateMarkData(); + } + + return mark; +}; + +// 生成 20 条模拟数据 +export const mockMarks: Mark[] = Array.from({ length: 20 }, () => generateMark()); + +// 生成每种类型的示例数据 +export const mockMarksByType: Record = ensureType.reduce((acc, type) => { + acc[type] = generateMarkWithType(type); + return acc; +}, {} as Record); + +// 导出生成器函数 +export { + generateMark, + generateMarkWithType, + generateMarkData, + generateMarkDataNode, + generateFileAttachment +}; \ No newline at end of file diff --git a/web/src/apps/muse/base/table/DetailModal.tsx b/web/src/apps/muse/base/table/DetailModal.tsx new file mode 100644 index 0000000..c9279be --- /dev/null +++ b/web/src/apps/muse/base/table/DetailModal.tsx @@ -0,0 +1,153 @@ +import React from 'react'; +import { Mark } from '../mock/collection'; +import './modal.css'; + +interface DetailModalProps { + visible: boolean; + data: Mark | null; + onClose: () => void; +} + +export const DetailModal: React.FC = ({ visible, data, onClose }) => { + if (!visible || !data) return null; + + return ( +
+
e.stopPropagation()}> +
+

详情信息

+ +
+ +
+
+

基本信息

+
+
+ + {data.title} +
+
+ + {data.markType} +
+
+ + {data.uname} +
+
+ + + {data.config.visibility === 'public' ? '公开' : + data.config.visibility === 'private' ? '私有' : '受限'} + +
+
+
+ +
+

描述

+

{data.description}

+
+ +
+

标签

+
+ {data.tags.map((tag, index) => ( + {tag} + ))} +
+
+ +
+

时间信息

+
+
+ + {new Date(data.markedAt).toLocaleString('zh-CN')} +
+
+ + {new Date(data.createdAt).toLocaleString('zh-CN')} +
+
+ + {new Date(data.updatedAt).toLocaleString('zh-CN')} +
+
+ + v{data.version} +
+
+
+ + {data.fileList.length > 0 && ( +
+

附件文件 ({data.fileList.length})

+
+ {data.fileList.map((file, index) => ( +
+
+ {file.name} + {formatFileSize(file.size)} +
+ {file.type} +
+ ))} +
+
+ )} + +
+

数据摘要

+

{data.data.summary || data.summary}

+
+ + {data.config.allowComments !== undefined && ( +
+

权限设置

+
+
+ + + {data.config.allowComments ? '是' : '否'} + +
+
+ + + {data.config.allowDownload ? '是' : '否'} + +
+ {data.config.expiredAt && ( +
+ + {new Date(data.config.expiredAt).toLocaleString('zh-CN')} +
+ )} +
+
+ )} +
+ +
+ + +
+
+
+ ); +}; + +// 格式化文件大小 +function formatFileSize(bytes: number): string { + if (bytes === 0) return '0 B'; + + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; +} \ No newline at end of file diff --git a/web/src/apps/muse/base/table/DragSelection.md b/web/src/apps/muse/base/table/DragSelection.md new file mode 100644 index 0000000..b702b69 --- /dev/null +++ b/web/src/apps/muse/base/table/DragSelection.md @@ -0,0 +1,172 @@ +# 表格拖拽多选功能 + +## 功能概述 + +表格组件现在支持拖拽多选功能,用户可以通过鼠标拖拽来快速选择多行数据,提升批量操作的效率。 + +## 功能特性 + +### ✅ 已实现的功能 + +1. **基础拖拽选择** + - 在表格行上拖拽可以选择多行 + - 实时显示选择框和选中状态 + - 拖拽结束后更新选中状态 + +2. **智能交互** + - 避免在复选框和操作按钮区域启动拖拽 + - 设置最小拖拽距离阈值,避免意外触发 + - 拖拽过程中禁用文本选择 + +3. **键盘修饰键支持** + - **Ctrl/Cmd + 拖拽**:追加选择模式(切换选中状态) + - **Shift + 点击复选框**:范围选择模式 + - **Ctrl/Cmd + A**:全选 + - **Escape**:取消拖拽或清空选择 + +4. **虚拟滚动兼容** + - 正确处理虚拟滚动的坐标转换 + - 考虑滚动位置的行索引计算 + +5. **视觉反馈** + - 拖拽选择框的实时显示 + - 选中行的高亮效果 + - 拖拽过程中的样式变化 + - 用户操作提示 + +## 使用方法 + +### 基本配置 + +```typescript +import { Table } from './base/table/Table'; +import { RowSelection } from './base/table/types'; + +const rowSelection: RowSelection = { + type: 'checkbox', + selectedRowKeys, + onChange: (selectedRowKeys, selectedRows) => { + // 处理选择变化 + setSelectedRowKeys(selectedRowKeys); + }, + dragSelection: { + enabled: true, // 启用拖拽多选 + multi: true, // 支持多选 + onDragStart: (startRowIndex) => { + console.log('开始拖拽选择'); + }, + onDragEnd: (selectedRows) => { + console.log('拖拽选择结束'); + }, + }, +}; + + +``` + +### 配置选项 + +#### `DragSelectionConfig` + +```typescript +interface DragSelectionConfig { + enabled?: boolean; // 是否启用拖拽选择,默认为 true + multi?: boolean; // 是否支持多选,默认为 true + onDragStart?: (startRowIndex: number) => void; // 拖拽开始回调 + onDragEnd?: (selectedRows: Mark[]) => void; // 拖拽结束回调 +} +``` + +### 用户操作指南 + +1. **基础拖拽选择** + - 在表格行(非复选框/操作按钮区域)按下鼠标左键 + - 拖拽至目标行 + - 释放鼠标完成选择 + +2. **追加选择** + - 按住 `Ctrl`(Windows/Linux)或 `Cmd`(Mac)键 + - 进行拖拽选择 + - 已选中的行会切换状态(选中变未选中,未选中变选中) + +3. **范围选择** + - 先点击一个复选框选中一行 + - 按住 `Shift` 键 + - 点击另一个复选框 + - 两行之间的所有行都会被选中 + +4. **键盘快捷键** + - `Ctrl/Cmd + A`:全选所有行 + - `Escape`:取消当前拖拽操作或清空所有选择 + +## 技术实现 + +### 核心组件 + +1. **状态管理** + - `DragSelectionState`:管理拖拽状态 + - 鼠标位置跟踪 + - 选择范围计算 + +2. **事件处理** + - `handleMouseDown`:开始拖拽检测 + - `handleMouseMove`:拖拽过程处理 + - `handleMouseUp`:完成选择操作 + +3. **坐标转换** + - 虚拟滚动坐标映射 + - 行索引计算 + - 碰撞检测算法 + +### 样式类名 + +- `.row-selected`:选中行样式 +- `.row-drag-selected`:拖拽选中行样式 +- `.drag-selection-box`:拖拽选择框样式 +- `.drag-hint`:操作提示样式 + +## 性能优化 + +1. **事件防抖** + - 设置拖拽距离阈值 + - 避免频繁状态更新 + +2. **虚拟滚动适配** + - 只处理可视区域内的行 + - 优化大数据量场景 + +3. **内存管理** + - 及时清理事件监听器 + - 合理的状态重置 + +## 兼容性 + +- ✅ 支持现有的复选框选择 +- ✅ 兼容虚拟滚动 +- ✅ 支持排序和筛选 +- ✅ 响应式设计 +- ✅ 键盘导航友好 + +## 示例代码 + +查看 `DragSelectionExample.tsx` 文件获取完整的使用示例。 + +## 注意事项 + +1. 拖拽选择不会在复选框和操作按钮区域启动 +2. 需要设置合适的行高以确保拖拽体验 +3. 大数据量时建议启用虚拟滚动 +4. 移动端设备可能需要额外的触摸事件处理 + +## 未来规划 + +- [ ] 触摸设备支持 +- [ ] 自定义拖拽选择框样式 +- [ ] 更多键盘快捷键 +- [ ] 拖拽选择动画效果 +- [ ] 选择统计和操作面板 \ No newline at end of file diff --git a/web/src/apps/muse/base/table/DragSelectionExample.tsx b/web/src/apps/muse/base/table/DragSelectionExample.tsx new file mode 100644 index 0000000..8c88af7 --- /dev/null +++ b/web/src/apps/muse/base/table/DragSelectionExample.tsx @@ -0,0 +1,115 @@ +// 使用拖拽多选功能的示例 + +import React, { useState } from 'react'; +import { Table } from './Table'; +import { Mark } from '../mock/collection'; +import { TableColumn, RowSelection } from './types'; + +const ExampleTable: React.FC = () => { + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + + // 示例数据 + const data: Mark[] = [ + { + id: '1', + title: '示例标记 1', + description: '这是第一个标记的描述', + data: { content: '这是第一个标记' }, + createdAt: new Date(), + updatedAt: new Date() + }, + { + id: '2', + title: '示例标记 2', + description: '这是第二个标记的描述', + data: { content: '这是第二个标记' }, + createdAt: new Date(), + updatedAt: new Date() + }, + { + id: '3', + title: '示例标记 3', + description: '这是第三个标记的描述', + data: { content: '这是第三个标记' }, + createdAt: new Date(), + updatedAt: new Date() + }, + // ... 更多数据 + ]; + + // 表格列配置 + const columns: TableColumn[] = [ + { + key: 'title', + title: '标题', + dataIndex: 'title', + width: 200, + sortable: true, + }, + { + key: 'description', + title: '描述', + dataIndex: 'description', + width: 300, + }, + { + key: 'createdAt', + title: '创建时间', + dataIndex: 'createdAt', + width: 180, + render: (value: Date) => value.toLocaleString(), + }, + ]; + + // 行选择配置,启用拖拽多选 + const rowSelection: RowSelection = { + type: 'checkbox', + selectedRowKeys, + onChange: (selectedRowKeys: React.Key[], selectedRows: Mark[]) => { + console.log('选中的行键:', selectedRowKeys); + console.log('选中的行数据:', selectedRows); + setSelectedRowKeys(selectedRowKeys); + }, + dragSelection: { + enabled: true, // 启用拖拽多选 + multi: true, // 支持多选 + onDragStart: (startRowIndex: number) => { + console.log('开始拖拽选择,起始行索引:', startRowIndex); + }, + onDragEnd: (selectedRows: Mark[]) => { + console.log('拖拽选择结束,选中的行:', selectedRows); + }, + }, + }; + + return ( +
+

表格拖拽多选示例

+

+ 使用说明: +
+ • 单击复选框进行单个选择 +
+ • 在表格行上拖拽可以选择多行(避开复选框和操作按钮) +
+ • 按住 Ctrl/Cmd 键 + 拖拽可以追加选择 +
+ • 按住 Shift 键 + 点击复选框可以进行范围选择 +
+ • 按 Ctrl/Cmd + A 可以全选 +
+ • 按 Escape 键可以取消选择或取消拖拽操作 +

+ +
+ + ); +}; + +export default ExampleTable; \ No newline at end of file diff --git a/web/src/apps/muse/base/table/README.md b/web/src/apps/muse/base/table/README.md new file mode 100644 index 0000000..4b639d2 --- /dev/null +++ b/web/src/apps/muse/base/table/README.md @@ -0,0 +1,176 @@ +# 数据管理表格组件 + +这是一个功能完整的React表格组件,支持多选、排序、虚拟滚动、操作等功能,并集成了Mock数据。 + +## 功能特性 + +### ✅ 已实现功能 + +1. **数据展示** + - 支持多种数据类型展示(标题、类型、标签、创建者等) + - 自定义列渲染(类型徽章、标签展示等) + - 响应式设计,适配移动端 + +2. **多选功能** + - 支持单行选择和全选 + - 批量操作(批量删除) + - 选择状态实时显示 + +3. **排序功能** + - 支持多列排序(标题、类型、创建者、创建时间等) + - 升序/降序/取消排序 + - 排序状态可视化指示 + +4. **虚拟滚动功能** + - 基于 react-virtualized 实现高性能虚拟滚动 + - 支持大量数据展示而不影响性能 + - 可配置行高和表格高度 + - 固定表头,滚动内容区域 + +5. **操作功能** + - 详情查看(弹窗形式) + - 编辑功能 + - 删除功能(单个/批量) + - 删除确认对话框 + +6. **详情模态框** + - 完整的数据信息展示 + - 分区域显示(基本信息、描述、标签、时间信息等) + - 附件文件列表 + - 权限设置显示 + +## 文件结构 + +``` +base/table/ +├── index.tsx # 主组件入口,集成所有功能 +├── Table.tsx # 基础表格组件 +├── DetailModal.tsx # 详情查看模态框 +├── types.ts # TypeScript类型定义 +├── table.css # 表格样式 +└── modal.css # 模态框样式 +``` + +## 使用的Mock数据 + +数据来源:`base/mock/collection.ts` +- 20条模拟的Mark记录 +- 包含完整的用户、文件、配置等信息 +- 支持各种数据类型和状态 + +## 组件特色 + +### 1. 类型安全 +- 完整的TypeScript类型定义 +- 严格的类型检查 +- 良好的IDE支持 + +### 2. 用户体验 +- 直观的操作界面 +- 实时的状态反馈 +- 响应式设计 +- 加载状态和空状态处理 + +### 3. 数据展示 +- 多种数据类型的可视化展示 +- 颜色编码的类型和状态 +- 格式化的时间和文件大小 + +### 4. 交互功能 +- 丰富的操作按钮 +- 确认对话框 +- 详情查看弹窗 +- 批量操作支持 + +## 技术实现 + +### 状态管理 +- 使用React Hooks进行状态管理 +- 分离的数据状态和UI状态 +- 受控组件模式 + +### 样式设计 +- 现代化的UI设计 +- 一致的视觉风格 +- 响应式布局 +- 无障碍访问支持 + +### 数据处理 +- 客户端排序和分页 +- 嵌套数据访问 +- 数据格式化和转换 + +## 快速开始 + +1. 确保已安装依赖: + ```bash + pnpm install + ``` + +2. 启动开发服务器: + ```bash + npm run dev + ``` + +3. 访问表格页面查看效果 + +## 自定义配置 + +### 添加新列 +在 `index.tsx` 中的 `columns` 数组中添加新的列配置: + +```tsx +{ + key: 'newColumn', + title: '新列', + dataIndex: 'fieldName', + width: 120, + sortable: true, + render: (value, record) => { + // 自定义渲染逻辑 + return {value}; + } +} +``` + +### 添加新操作 +在 `actions` 数组中添加新的操作按钮: + +```tsx +{ + key: 'newAction', + label: '新操作', + type: 'primary', + icon: '🔧', + onClick: (record) => { + // 操作逻辑 + } +} +``` + +### 修改虚拟滚动配置 +调整 `virtualScrollConfig` 对象的属性: + +```tsx +const virtualScrollConfig = { + rowHeight: 60, // 行高度 + height: 600 // 表格容器高度 +}; +``` + +## 性能优化 + +1. **虚拟滚动**:已实现基于 react-virtualized 的虚拟滚动,支持大量数据高性能展示 +2. **懒加载**:支持服务端分页和按需加载 +3. **缓存**:实现数据缓存机制 +4. **防抖**:搜索和过滤功能添加防抖处理 + +## 后续优化建议 + +1. **搜索功能**:添加全局搜索和列过滤 +2. **导出功能**:支持数据导出为Excel/CSV +3. **列配置**:支持用户自定义显示列 +4. **主题配置**:支持多主题切换 +5. **国际化**:添加多语言支持 + +这个表格组件提供了现代化的数据管理界面,具备企业级应用所需的核心功能。 \ No newline at end of file diff --git a/web/src/apps/muse/base/table/Table.tsx b/web/src/apps/muse/base/table/Table.tsx new file mode 100644 index 0000000..fa36a3c --- /dev/null +++ b/web/src/apps/muse/base/table/Table.tsx @@ -0,0 +1,574 @@ +import React, { useState, useMemo, useRef, useCallback, useEffect } from 'react'; +import { AutoSizer, List } from 'react-virtualized'; +import { Mark } from '../mock/collection'; +import { TableProps, SortState, DragSelectionState } from './types'; +import './table.css'; + +// 虚拟滚动常量 +const DEFAULT_ROW_HEIGHT = 48; // 每行高度 +const HEADER_HEIGHT = 48; // 表头高度 + +export const Table: React.FC = ({ + data, + columns, + loading = false, + rowSelection, + virtualScroll, + actions, + onSort +}) => { + const rowHeight = virtualScroll?.rowHeight || DEFAULT_ROW_HEIGHT; + const [sortState, setSortState] = useState({ field: null, order: null }); + + // 拖拽选择状态 + const [dragState, setDragState] = useState({ + isDragging: false, + startPosition: null, + endPosition: null, + startRowIndex: null, + dragRect: null, + selectedDuringDrag: new Set() + }); + + // DOM 引用 + const tableBodyRef = useRef(null); + const selectionBoxRef = useRef(null); + const listRef = useRef(null); + + // 拖拽选择配置 + const dragSelectionEnabled = rowSelection?.dragSelection?.enabled !== false; + + // 处理排序 + const handleSort = (field: string) => { + let newOrder: 'asc' | 'desc' | null = 'asc'; + + if (sortState.field === field) { + if (sortState.order === 'asc') { + newOrder = 'desc'; + } else if (sortState.order === 'desc') { + newOrder = null; + } + } + + const newSortState = { field: newOrder ? field : null, order: newOrder }; + setSortState(newSortState); + onSort?.(newSortState.field!, newSortState.order!); + }; + + // 排序后的数据 + const sortedData = useMemo(() => { + if (!sortState.field || !sortState.order) return data; + + return [...data].sort((a, b) => { + const aVal = getNestedValue(a, sortState.field!); + const bVal = getNestedValue(b, sortState.field!); + + if (aVal < bVal) return sortState.order === 'asc' ? -1 : 1; + if (aVal > bVal) return sortState.order === 'asc' ? 1 : -1; + return 0; + }); + }, [data, sortState]); + + // 当前显示的数据(移除分页,直接使用排序后的数据) + const displayData = sortedData; + + // 全选/取消全选 + const handleSelectAll = (checked: boolean) => { + if (!rowSelection) return; + + const allKeys = displayData.map(item => item.id); + const selectedKeys = checked ? allKeys : []; + const selectedRows = checked ? displayData : []; + + rowSelection.onChange?.(selectedKeys, selectedRows); + }; + + // 上次点击的行索引(用于Shift范围选择) + const lastClickedRowRef = useRef(null); + + // 单行选择 - 支持Shift范围选择 + const handleRowSelect = (record: Mark, checked: boolean, event?: React.ChangeEvent) => { + if (!rowSelection) return; + + const currentKeys = rowSelection.selectedRowKeys || []; + const recordIndex = displayData.findIndex(item => item.id === record.id); + + // 处理Shift键范围选择 + if (event?.nativeEvent && (event.nativeEvent as any).shiftKey && lastClickedRowRef.current !== null) { + const startIndex = Math.min(lastClickedRowRef.current, recordIndex); + const endIndex = Math.max(lastClickedRowRef.current, recordIndex); + + const rangeKeys = displayData.slice(startIndex, endIndex + 1).map(item => item.id); + const newKeys = checked + ? [...new Set([...currentKeys, ...rangeKeys])] + : currentKeys.filter(key => !rangeKeys.includes(key as string)); + + const selectedRows = data.filter(item => newKeys.includes(item.id)); + rowSelection.onChange?.(newKeys, selectedRows); + } else { + // 普通单行选择 + const newKeys = checked + ? [...currentKeys, record.id] + : currentKeys.filter(key => key !== record.id); + + const selectedRows = data.filter(item => newKeys.includes(item.id)); + rowSelection.onChange?.(newKeys, selectedRows); + } + + // 更新上次点击的行索引 + lastClickedRowRef.current = recordIndex; + }; + + // 获取嵌套值 + const getNestedValue = (obj: any, path: string) => { + return path.split('.').reduce((o, p) => o?.[p], obj); + }; + + // 根据坐标获取行索引(考虑虚拟滚动的滚动位置) + const getRowIndexFromPosition = useCallback((y: number): number => { + if (!tableBodyRef.current || !listRef.current) return -1; + + const rect = tableBodyRef.current.getBoundingClientRect(); + const relativeY = y - rect.top; + + // 获取当前滚动位置 + const scrollTop = (listRef.current as any).Grid?._scrollingContainer?.scrollTop || 0; + + // 计算实际的行索引,考虑滚动位置 + const actualY = relativeY + scrollTop; + const rowIndex = Math.floor(actualY / rowHeight); + + return Math.max(0, Math.min(rowIndex, displayData.length - 1)); + }, [rowHeight, displayData.length]); + + // 计算拖拽选择范围内的行 + const getRowsInDragRange = useCallback((startY: number, endY: number): number[] => { + if (!tableBodyRef.current) return []; + + const minY = Math.min(startY, endY); + const maxY = Math.max(startY, endY); + + const startRowIndex = getRowIndexFromPosition(minY); + const endRowIndex = getRowIndexFromPosition(maxY); + + const rows: number[] = []; + for (let i = startRowIndex; i <= endRowIndex; i++) { + if (i >= 0 && i < displayData.length) { + rows.push(i); + } + } + + return rows; + }, [getRowIndexFromPosition, displayData.length]); + + // 检查拖拽选择框与行的碰撞检测 + const isRowInDragSelection = useCallback((rowIndex: number): boolean => { + if (!dragState.dragRect || !tableBodyRef.current) return false; + + const rect = tableBodyRef.current.getBoundingClientRect(); + const rowTop = rowIndex * rowHeight; + const rowBottom = rowTop + rowHeight; + + // 将拖拽选择框坐标转换为相对于表格的坐标 + const selectionTop = dragState.dragRect.top - rect.top; + const selectionBottom = dragState.dragRect.bottom - rect.top; + + // 检查行和选择框的垂直重叠 + return !(rowBottom < selectionTop || rowTop > selectionBottom); + }, [dragState.dragRect, rowHeight]); + + // 更新拖拽选择状态 + const updateDragSelection = useCallback((currentPosition: { x: number; y: number }) => { + if (!dragState.isDragging || !dragState.startPosition) return; + + const rowsInRange = getRowsInDragRange(dragState.startPosition.y, currentPosition.y); + const newSelectedKeys = new Set(rowsInRange.map(index => displayData[index].id)); + + setDragState(prev => ({ + ...prev, + endPosition: currentPosition, + selectedDuringDrag: newSelectedKeys, + dragRect: { + left: Math.min(dragState.startPosition!.x, currentPosition.x), + top: Math.min(dragState.startPosition!.y, currentPosition.y), + right: Math.max(dragState.startPosition!.x, currentPosition.x), + bottom: Math.max(dragState.startPosition!.y, currentPosition.y), + width: Math.abs(currentPosition.x - dragState.startPosition!.x), + height: Math.abs(currentPosition.y - dragState.startPosition!.y), + x: Math.min(dragState.startPosition!.x, currentPosition.x), + y: Math.min(dragState.startPosition!.y, currentPosition.y), + toJSON: () => ({}) + } as DOMRect + })); + }, [dragState.isDragging, dragState.startPosition, getRowsInDragRange, displayData]); + + // 拖拽最小距离阈值(像素) + const DRAG_THRESHOLD = 5; + + // 鼠标按下事件处理 + const handleMouseDown = useCallback((event: React.MouseEvent) => { + if (!dragSelectionEnabled || !rowSelection || event.button !== 0) return; + + // 如果点击的是复选框或操作按钮,不启动拖拽选择 + const target = event.target as HTMLElement; + if (target.tagName === 'INPUT' || target.closest('.selection-column') || target.closest('.actions-column')) { + return; + } + + const rect = tableBodyRef.current?.getBoundingClientRect(); + if (!rect) return; + + const startPosition = { + x: event.clientX, + y: event.clientY + }; + + const startRowIndex = getRowIndexFromPosition(event.clientY); + + setDragState({ + isDragging: false, // 先不设为true,等达到阈值再设置 + startPosition, + endPosition: startPosition, + startRowIndex, + dragRect: null, + selectedDuringDrag: new Set() + }); + + event.preventDefault(); + }, [dragSelectionEnabled, rowSelection, getRowIndexFromPosition]); + + // 鼠标移动事件处理 + const handleMouseMove = useCallback((event: MouseEvent) => { + if (!dragState.startPosition) return; + + const currentPosition = { + x: event.clientX, + y: event.clientY + }; + + // 检查是否超过拖拽阈值 + const deltaX = Math.abs(currentPosition.x - dragState.startPosition.x); + const deltaY = Math.abs(currentPosition.y - dragState.startPosition.y); + const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + + if (!dragState.isDragging && distance > DRAG_THRESHOLD) { + // 达到阈值,开始拖拽选择 + setDragState(prev => ({ + ...prev, + isDragging: true + })); + + // 调用拖拽开始回调 + rowSelection?.dragSelection?.onDragStart?.(dragState.startRowIndex!); + } + + if (dragState.isDragging) { + updateDragSelection(currentPosition); + } + }, [dragState.startPosition, dragState.isDragging, dragState.startRowIndex, updateDragSelection, rowSelection]); + + // 鼠标抬起事件处理 + const handleMouseUp = useCallback((event: MouseEvent) => { + if (!dragState.startPosition) return; + + // 如果没有开始拖拽(未达到阈值),直接重置状态 + if (!dragState.isDragging) { + setDragState({ + isDragging: false, + startPosition: null, + endPosition: null, + startRowIndex: null, + dragRect: null, + selectedDuringDrag: new Set() + }); + return; + } + + const selectedRows = displayData.filter(item => dragState.selectedDuringDrag.has(item.id)); + const selectedKeys = Array.from(dragState.selectedDuringDrag); + + // 处理选择模式 - Ctrl/Cmd 键多选,Shift 键范围选择 + const isCtrlOrCmd = event.ctrlKey || event.metaKey; + const currentSelectedKeys = rowSelection?.selectedRowKeys || []; + + let newSelectedKeys: React.Key[]; + if (isCtrlOrCmd) { + // Ctrl/Cmd + 拖拽:切换选择状态 + const existingKeys = new Set(currentSelectedKeys); + selectedKeys.forEach(key => { + if (existingKeys.has(key)) { + existingKeys.delete(key); + } else { + existingKeys.add(key); + } + }); + newSelectedKeys = Array.from(existingKeys); + } else { + // 普通拖拽:替换选择 + newSelectedKeys = selectedKeys; + } + + const finalSelectedRows = displayData.filter(item => newSelectedKeys.includes(item.id)); + + // 更新选择状态 + rowSelection?.onChange?.(newSelectedKeys, finalSelectedRows); + + // 调用拖拽结束回调 + rowSelection?.dragSelection?.onDragEnd?.(finalSelectedRows); + + // 重置拖拽状态 + setDragState({ + isDragging: false, + startPosition: null, + endPosition: null, + startRowIndex: null, + dragRect: null, + selectedDuringDrag: new Set() + }); + }, [dragState.isDragging, dragState.selectedDuringDrag, displayData, rowSelection]); + + // 键盘事件处理 + const handleKeyDown = useCallback((event: KeyboardEvent) => { + if (!rowSelection) return; + + // Ctrl/Cmd + A 全选 + if ((event.ctrlKey || event.metaKey) && event.key === 'a') { + event.preventDefault(); + handleSelectAll(true); + return; + } + + // Escape 取消选择 + if (event.key === 'Escape') { + if (dragState.isDragging) { + // 取消拖拽 + setDragState({ + isDragging: false, + startPosition: null, + endPosition: null, + startRowIndex: null, + dragRect: null, + selectedDuringDrag: new Set() + }); + } else { + // 清空选择 + handleSelectAll(false); + } + return; + } + }, [rowSelection, dragState.isDragging, handleSelectAll]); + + // 添加全局鼠标事件监听器 + useEffect(() => { + if (dragState.startPosition) { + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + + if (dragState.isDragging) { + document.body.style.userSelect = 'none'; // 禁用文本选择 + } + + return () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + document.body.style.userSelect = ''; + }; + } + }, [dragState.startPosition, dragState.isDragging, handleMouseMove, handleMouseUp]); + + // 添加键盘事件监听器 + useEffect(() => { + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [handleKeyDown]); + + // 渲染虚拟滚动行 + const rowRenderer = ({ index, key, style }: any) => { + const record = displayData[index]; + const isSelected = selectedKeys.includes(record.id); + const isDragSelected = dragState.selectedDuringDrag.has(record.id); + const isHighlighted = isSelected || isDragSelected; + + return ( +
+ {rowSelection && ( +
+ handleRowSelect(record, e.target.checked, e)} + disabled={rowSelection.getCheckboxProps?.(record)?.disabled} + /> +
+ )} + {columns.map(column => ( +
+ {column.render + ? column.render(getNestedValue(record, column.dataIndex), record, index) + : getNestedValue(record, column.dataIndex) + } +
+ ))} + {actions && actions.length > 0 && ( +
+
+ {actions.map(action => ( + + ))} +
+
+ )} +
+ ); + }; + + if (loading) { + return ( +
+
+ 加载中... +
+ ); + } + + const selectedKeys = rowSelection?.selectedRowKeys || []; + const isAllSelected = displayData.length > 0 && displayData.every(item => selectedKeys.includes(item.id)); + const isIndeterminate = selectedKeys.length > 0 && !isAllSelected; + + return ( +
+ {/* 表格工具栏 */} + {rowSelection && selectedKeys.length > 0 && ( +
+
+ 已选择 {selectedKeys.length} 项 + {dragSelectionEnabled && ( + + (支持拖拽多选,按住Ctrl/Cmd键可追加选择,按住Shift键可范围选择) + + )} +
+
+ + +
+
+ )} + + {/* 表格 */} +
+ {/* 固定表头 */} +
+
+ {rowSelection && ( +
+ { + if (input) input.indeterminate = isIndeterminate; + }} + onChange={(e) => handleSelectAll(e.target.checked)} + /> +
+ )} + {columns.map(column => ( +
+
+ {column.title} + {column.sortable && ( +
handleSort(column.dataIndex)} + > + + +
+ )} +
+
+ ))} + {actions && actions.length > 0 && ( +
操作
+ )} +
+
+ + {/* 虚拟滚动内容区域 */} +
+ {displayData.length > 0 ? ( + + {({ height, width }) => ( + + )} + + ) : ( +
+
📭
+

暂无数据

+
+ )} + + {/* 拖拽选择框 */} + {dragState.isDragging && dragState.dragRect && ( +
+ )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/web/src/apps/muse/base/table/index.tsx b/web/src/apps/muse/base/table/index.tsx new file mode 100644 index 0000000..660dd76 --- /dev/null +++ b/web/src/apps/muse/base/table/index.tsx @@ -0,0 +1,270 @@ +import React, { useState, useMemo, useEffect } from 'react'; +import { Eye, Edit, Trash2 } from 'lucide-react'; +import { Table } from './Table'; +import { DetailModal } from './DetailModal'; +import { Mark } from '../mock/collection'; +import { TableColumn, ActionButton } from './types'; + +type Props = { + dataSource?: Mark[]; +} +export const Base = (props: Props) => { + const { dataSource = [] } = props; + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [data, setData] = useState([]); + const [detailModalVisible, setDetailModalVisible] = useState(false); + const [currentRecord, setCurrentRecord] = useState(null); + + useEffect(() => { + if (dataSource) { + setData(dataSource); + } + }, [dataSource]); + // 表格列配置 + const columns: TableColumn[] = [ + { + key: 'title', + title: '标题', + dataIndex: 'title', + width: 300, + sortable: true, + render: (value: string, record: Mark) => ( +
+
+ {value} +
+
+ {record.description?.slice?.(0, 60)}... +
+
+ ) + }, + { + key: 'markType', + title: '类型', + dataIndex: 'markType', + width: 100, + sortable: true, + render: (value: string) => { + if (!value) return '' + return ( + + {value} + + ) + } + }, + { + key: 'tags', + title: '标签', + dataIndex: 'tags', + width: 200, + render: (tags: string[] = []) => ( +
+ {tags?.slice?.(0, 3).map((tag, index) => ( + + {tag} + + ))} + {tags.length > 3 && ( + + +{tags.length - 3} + + )} +
+ ) + }, + { + key: 'uname', + title: '创建者', + dataIndex: 'uname', + width: 120, + sortable: true + }, + { + key: 'createdAt', + title: '创建时间', + dataIndex: 'createdAt', + width: 180, + sortable: true, + render: (value: Date) => new Date(value).toLocaleString('zh-CN') + }, + { + key: 'data.visibility', + title: '可见性', + dataIndex: 'data.visibility', + width: 100, + render: (value: string) => ( + + {value === 'public' ? '公开' : value === 'private' ? '私有' : '受限'} + + ) + } + ]; + + // 操作按钮配置 + const actions: ActionButton[] = [ + { + key: 'view', + label: '详情', + icon: , + onClick: (record: Mark) => { + handleViewDetail(record); + } + }, + { + key: 'edit', + icon: , + label: '编辑', + onClick: (record: Mark) => { + handleEdit(record); + } + }, + { + key: 'delete', + label: '删除', + icon: , + onClick: (record: Mark) => { + handleDelete(record); + } + } + ]; + + // 获取类型颜色 + const getTypeColor = (type: string): string => { + const colors: Record = { + markdown: '#1890ff', + json: '#52c41a', + html: '#fa8c16', + image: '#eb2f96', + video: '#722ed1', + audio: '#13c2c2', + code: '#666', + link: '#1890ff', + file: '#999' + }; + return colors[type] || '#999'; + }; + + // 处理详情查看 + const handleViewDetail = (record: Mark) => { + setCurrentRecord(record); + setDetailModalVisible(true); + }; + + // 处理编辑 + const handleEdit = (record: Mark) => { + alert(`编辑: ${record.title}`); + // 这里可以打开编辑对话框或跳转到编辑页面 + }; + + // 处理删除 + const handleDelete = (record: Mark) => { + if (window.confirm(`确定要删除"${record.title}"吗?`)) { + setData(prevData => prevData.filter(item => item.id !== record.id)); + // 如果当前选中的项包含被删除的项,也要从选中列表中移除 + setSelectedRowKeys(prev => prev.filter(key => key !== record.id)); + } + }; + + // 处理批量删除 + const handleBatchDelete = () => { + if (selectedRowKeys.length === 0) return; + + if (window.confirm(`确定要删除选中的 ${selectedRowKeys.length} 项吗?`)) { + setData(prevData => prevData.filter(item => !selectedRowKeys.includes(item.id))); + setSelectedRowKeys([]); + } + }; + + // 排序处理 + const handleSort = (field: string, order: 'asc' | 'desc' | null) => { + if (!order) { + setData(dataSource); // 重置为原始顺序 + return; + } + + const sortedData = [...data].sort((a, b) => { + const getNestedValue = (obj: any, path: string) => { + return path.split('.').reduce((o, p) => o?.[p], obj); + }; + + const aVal = getNestedValue(a, field); + const bVal = getNestedValue(b, field); + + if (aVal < bVal) return order === 'asc' ? -1 : 1; + if (aVal > bVal) return order === 'asc' ? 1 : -1; + return 0; + }); + + setData(sortedData); + }; + + // 虚拟滚动配置 + const virtualScrollConfig = { + rowHeight: 60, // 因为有两行内容,需要更高的行高 + // 移除固定高度,让表格自适应容器高度 + }; + + return ( +
+
+ {/* 固定头部区域 */} +
+

数据管理表格

+

+ 支持多选、排序、虚拟滚动等功能的数据表格示例 +

+
+ + {/* 表格容器 - 占据剩余空间并支持滚动 */} +
+
{ + setSelectedRowKeys(keys); + } + }} + virtualScroll={virtualScrollConfig} + onSort={handleSort} + /> + + + { + setDetailModalVisible(false); + setCurrentRecord(null); + }} + /> + + + ); +}; \ No newline at end of file diff --git a/web/src/apps/muse/base/table/modal.css b/web/src/apps/muse/base/table/modal.css new file mode 100644 index 0000000..275dd5c --- /dev/null +++ b/web/src/apps/muse/base/table/modal.css @@ -0,0 +1,305 @@ +/* 模态框样式 */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 20px; +} + +.modal-content { + background: #fff; + border-radius: 8px; + max-width: 800px; + width: 100%; + max-height: 90vh; + overflow: hidden; + display: flex; + flex-direction: column; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 24px; + border-bottom: 1px solid #e8e8e8; + background: #fafafa; +} + +.modal-header h3 { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #333; +} + +.modal-close { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: all 0.2s; +} + +.modal-close:hover { + background: #f0f0f0; + color: #333; +} + +.modal-body { + flex: 1; + overflow-y: auto; + padding: 24px; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 12px; + padding: 16px 24px; + border-top: 1px solid #e8e8e8; + background: #fafafa; +} + +/* 详情部分 */ +.detail-section { + margin-bottom: 24px; +} + +.detail-section h4 { + margin: 0 0 12px 0; + font-size: 16px; + font-weight: 600; + color: #333; + border-bottom: 2px solid #1890ff; + padding-bottom: 8px; +} + +.detail-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 12px; +} + +.detail-item { + display: flex; + align-items: center; + gap: 8px; +} + +.detail-item label { + font-weight: 600; + color: #666; + min-width: 80px; + font-size: 14px; +} + +.detail-item span { + color: #333; + font-size: 14px; +} + +/* 类型徽章 */ +.type-badge { + padding: 4px 8px; + border-radius: 4px; + color: #fff; + font-size: 12px; + font-weight: 500; +} + +.type-markdown { background: #1890ff; } +.type-json { background: #52c41a; } +.type-html { background: #fa8c16; } +.type-image { background: #eb2f96; } +.type-video { background: #722ed1; } +.type-audio { background: #13c2c2; } +.type-code { background: #666; } +.type-link { background: #1890ff; } +.type-file { background: #999; } + +/* 可见性徽章 */ +.visibility-badge { + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; +} + +.visibility-public { + background: #f6ffed; + color: #52c41a; + border: 1px solid #b7eb8f; +} + +.visibility-private { + background: #fff2f0; + color: #ff4d4f; + border: 1px solid #ffccc7; +} + +.visibility-restricted { + background: #fffbe6; + color: #faad14; + border: 1px solid #ffe58f; +} + +/* 标签容器 */ +.tags-container { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.tag { + padding: 4px 8px; + background: #f0f0f0; + border-radius: 4px; + font-size: 12px; + color: #666; + border: 1px solid #d9d9d9; +} + +/* 文件列表 */ +.file-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.file-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px; + border: 1px solid #e8e8e8; + border-radius: 4px; + background: #fafafa; +} + +.file-info { + display: flex; + flex-direction: column; + gap: 4px; +} + +.file-name { + font-weight: 500; + color: #333; + font-size: 14px; +} + +.file-size { + font-size: 12px; + color: #999; +} + +.file-type { + padding: 2px 6px; + border-radius: 2px; + font-size: 10px; + font-weight: 500; + text-transform: uppercase; +} + +.file-type-self { + background: #e6f7ff; + color: #1890ff; +} + +.file-type-data { + background: #f6ffed; + color: #52c41a; +} + +.file-type-generate { + background: #fff7e6; + color: #fa8c16; +} + +/* 摘要文本 */ +.summary-text { + line-height: 1.6; + color: #666; + margin: 0; + padding: 12px; + background: #f9f9f9; + border-radius: 4px; + border-left: 4px solid #1890ff; +} + +/* 权限网格 */ +.permission-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 12px; +} + +.permission-item { + display: flex; + align-items: center; + gap: 8px; +} + +.permission-item label { + font-weight: 600; + color: #666; + font-size: 14px; +} + +.enabled { + color: #52c41a; + font-weight: 500; +} + +.disabled { + color: #ff4d4f; + font-weight: 500; +} + +/* 响应式 */ +@media (max-width: 768px) { + .modal-content { + margin: 10px; + max-height: 95vh; + } + + .modal-header, + .modal-body, + .modal-footer { + padding: 16px; + } + + .detail-grid { + grid-template-columns: 1fr; + } + + .permission-grid { + grid-template-columns: 1fr; + } + + .file-item { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .modal-footer { + flex-direction: column; + } +} \ No newline at end of file diff --git a/web/src/apps/muse/base/table/table.css b/web/src/apps/muse/base/table/table.css new file mode 100644 index 0000000..af00cf0 --- /dev/null +++ b/web/src/apps/muse/base/table/table.css @@ -0,0 +1,563 @@ +/* 表格容器 */ +.table-container { + background: #fff; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + height: calc(100% - 24px); /* 距离底部保持24px的间距 */ + margin-bottom: 24px; /* 底部间距 */ + display: flex; + flex-direction: column; + max-height: calc(100% - 24px); /* 添加最大高度限制 */ +} + +/* 工具栏 */ +.table-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + background: #f5f5f5; + border-bottom: 1px solid #e8e8e8; + flex-shrink: 0; /* 工具栏不压缩,保持固定高度 */ + min-height: 56px; /* 确保工具栏有固定的最小高度 */ + box-sizing: border-box; /* 包含padding和border在内的尺寸计算 */ +} + +.selected-info { + color: #666; + font-size: 14px; + display: flex; + flex-direction: column; + gap: 4px; +} + +.drag-hint { + font-size: 12px; + color: #999; + font-style: italic; +} + +.bulk-actions { + display: flex; + gap: 8px; +} + +/* 表格主体 - 虚拟滚动布局 */ +.table-wrapper { + display: flex; + flex-direction: column; + flex: 1; /* 占满剩余空间 */ + min-height: 200px; /* 最小高度,避免过小 */ + overflow: hidden; /* 防止溢出 */ + height: 0; /* 重要:配合flex: 1使用,确保正确计算可用空间 */ +} + +/* 固定表头容器 */ +.table-header-wrapper { + flex-shrink: 0; + border-bottom: 1px solid #e8e8e8; + background: #fafafa; + overflow: hidden; +} + +.table-header-row { + display: flex; + align-items: center; +} + +.table-header-cell { + display: flex; + align-items: center; + padding: 12px 16px; + border-right: 1px solid #e8e8e8; + font-weight: 600; + color: #333; + background: #fafafa; +} + +.table-header-cell:last-child { + border-right: none; +} + +.table-header-cell.sortable { + cursor: pointer; + user-select: none; +} + +/* 虚拟滚动内容区域 */ +.table-body-wrapper { + flex: 1; + overflow: hidden; + background: #fff; + min-height: 0; /* 重要:允许flex子项收缩 */ + position: relative; /* 为AutoSizer提供相对定位上下文 */ +} + +/* 拖拽选择框 */ +.drag-selection-box { + background: rgba(24, 144, 255, 0.1); + border: 2px dashed #1890ff; + border-radius: 4px; + z-index: 1000; + animation: dragBoxPulse 0.3s ease-in-out; +} + +@keyframes dragBoxPulse { + 0% { + transform: scale(0.95); + opacity: 0.8; + } + 50% { + transform: scale(1.02); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +/* 虚拟行样式 */ +.virtual-row { + display: flex; + align-items: center; + border-bottom: 1px solid #e8e8e8; + background: #fff; + transition: background-color 0.2s; + position: relative; +} + +.virtual-row:hover { + background: #f5f5f5; +} + +/* 选中行样式 */ +.virtual-row.row-selected { + background: #e6f7ff !important; + border-color: #91d5ff; +} + +.virtual-row.row-selected:hover { + background: #bae7ff !important; +} + +/* 拖拽选中行样式 */ +.virtual-row.row-drag-selected { + background: #f0f9ff !important; + border-color: #69c0ff; + box-shadow: inset 0 0 0 1px #40a9ff; +} + +.table-cell { + display: flex; + align-items: center; + padding: 12px 16px; + border-right: 1px solid #e8e8e8; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.table-cell:last-child { + border-right: none; +} + +/* 表头 */ +.table-header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.sortable { + cursor: pointer; + user-select: none; +} + +.sort-indicators { + display: flex; + flex-direction: column; + margin-left: 8px; +} + +.sort-arrow { + font-size: 10px; + line-height: 1; + color: #ccc; + cursor: pointer; + transition: color 0.2s; +} + +.sort-arrow.active { + color: #1890ff; +} + +.sort-up { + margin-bottom: 2px; +} + +/* 选择列 */ +.selection-column { + width: 48px; + text-align: center; + position: relative; +} + +.selection-column input[type="checkbox"] { + cursor: pointer; + transform: scale(1.1); + transition: all 0.2s; +} + +.selection-column input[type="checkbox"]:hover { + transform: scale(1.2); +} + +.selection-column input[type="checkbox"]:checked { + accent-color: #1890ff; +} + +/* 拖拽选择时的表格样式 */ +.table-body-wrapper[style*="cursor: crosshair"] { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +.table-body-wrapper[style*="cursor: crosshair"] .virtual-row { + pointer-events: none; +} + +.table-body-wrapper[style*="cursor: crosshair"] .selection-column, +.table-body-wrapper[style*="cursor: crosshair"] .actions-column { + pointer-events: auto; +} + +/* 操作列 */ +.actions-column { + width: auto; + min-width: 120px; + text-align: right; + padding: 8px 16px; +} + +.action-buttons { + position: relative; + display: flex; + gap: 6px; + justify-content: flex-end; + align-items: center; + flex-wrap: wrap; +} + +/* 按钮样式 */ +.btn { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 4px; + padding: 8px 12px; + min-width: 32px; + height: 32px; + border: 1px solid #d9d9d9; + border-radius: 6px; + background: #fff; + color: #333; + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + text-decoration: none; + user-select: none; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + outline: none; +} + +.btn:hover:not(:disabled) { + border-color: #40a9ff; + color: #40a9ff; + transform: translateY(-1px); + box-shadow: 0 4px 8px 0 rgba(64, 169, 255, 0.15); +} + +.btn:focus:not(:disabled) { + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgba(64, 169, 255, 0.2); +} + +.btn:active:not(:disabled) { + transform: translateY(0); + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1); +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none !important; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important; +} + +.btn-primary { + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + border-color: #1890ff; + color: #fff; +} + +.btn-primary:hover:not(:disabled) { + background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%); + border-color: #40a9ff; + color: #fff; + box-shadow: 0 4px 12px 0 rgba(24, 144, 255, 0.3); +} + +.btn-primary:focus:not(:disabled) { + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.3); +} + +.btn-danger { + background: linear-gradient(135deg, #ff4d4f 0%, #f5222d 100%); + border-color: #ff4d4f; + color: #fff; +} + +.btn-danger:hover:not(:disabled) { + background: linear-gradient(135deg, #ff7875 0%, #ff4d4f 100%); + border-color: #ff7875; + color: #fff; + box-shadow: 0 4px 12px 0 rgba(255, 77, 79, 0.3); +} + +.btn-danger:focus:not(:disabled) { + box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.3); +} + +.btn-success { + background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%); + border-color: #52c41a; + color: #fff; +} + +.btn-success:hover:not(:disabled) { + background: linear-gradient(135deg, #73d13d 0%, #52c41a 100%); + border-color: #73d13d; + color: #fff; + box-shadow: 0 4px 12px 0 rgba(82, 196, 26, 0.3); +} + +.btn-success:focus:not(:disabled) { + box-shadow: 0 0 0 2px rgba(82, 196, 26, 0.3); +} + +.btn-warning { + background: linear-gradient(135deg, #faad14 0%, #d48806 100%); + border-color: #faad14; + color: #fff; +} + +.btn-warning:hover:not(:disabled) { + background: linear-gradient(135deg, #ffc53d 0%, #faad14 100%); + border-color: #ffc53d; + color: #fff; + box-shadow: 0 4px 12px 0 rgba(250, 173, 20, 0.3); +} + +.btn-warning:focus:not(:disabled) { + box-shadow: 0 0 0 2px rgba(250, 173, 20, 0.3); +} + +.btn-icon { + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; +} + +/* Tooltip 样式 */ +.tooltip { + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + margin-bottom: 8px; + padding: 6px 10px; + background: rgba(0, 0, 0, 0.85); + color: #fff; + font-size: 12px; + font-weight: 400; + line-height: 1.4; + border-radius: 4px; + white-space: nowrap; + opacity: 0; + visibility: hidden; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + pointer-events: none; + z-index: 1000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.tooltip::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 4px solid transparent; + border-top-color: rgba(0, 0, 0, 0.85); +} + +.btn:hover .tooltip { + opacity: 1; + visibility: visible; + transform: translateX(-50%) translateY(-4px); +} + +/* 确保tooltip在不同位置的按钮中都能正确显示 */ +.action-buttons { + position: relative; +} + +/* 按钮尺寸变体 */ +.btn-small { + padding: 4px 8px; + min-width: 24px; + height: 24px; + font-size: 11px; +} + +.btn-small .btn-icon { + font-size: 12px; +} + +.btn-medium { + padding: 8px 12px; + min-width: 32px; + height: 32px; + font-size: 12px; +} + +.btn-medium .btn-icon { + font-size: 14px; +} + +.btn-large { + padding: 12px 16px; + min-width: 40px; + height: 40px; + font-size: 14px; +} + +.btn-large .btn-icon { + font-size: 16px; +} + +/* 空状态 */ +.empty-state { + text-align: center; + padding: 64px 16px; + color: #999; +} + +.empty-icon { + font-size: 48px; + margin-bottom: 16px; +} + +/* 加载状态 */ +.table-loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 64px 16px; + color: #666; +} + +.loading-spinner { + width: 32px; + height: 32px; + border: 3px solid #f0f0f0; + border-top: 3px solid #1890ff; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 16px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 响应式 */ +@media (max-width: 768px) { + .table-toolbar { + flex-direction: column; + gap: 12px; + align-items: stretch; + } + + .bulk-actions { + justify-content: flex-end; + } + + .table-wrapper { + min-height: 300px; /* 移动端最小高度,仍然使用flex: 1占满剩余空间 */ + } + + .action-buttons { + flex-direction: row; + gap: 4px; + justify-content: flex-end; + } + + .actions-column { + min-width: 80px; + padding: 6px 12px; + } + + .btn { + font-size: 11px; + padding: 6px 10px; + min-width: 28px; + height: 28px; + } + + .btn-icon { + font-size: 12px; + } + + /* 移动端tooltip调整 */ + .tooltip { + font-size: 11px; + padding: 4px 8px; + margin-bottom: 6px; + } + + .tooltip::after { + border-width: 3px; + } + + /* 移动端按钮尺寸调整 */ + .btn-small { + padding: 3px 6px; + min-width: 20px; + height: 20px; + font-size: 10px; + } + + .btn-medium { + padding: 6px 10px; + min-width: 28px; + height: 28px; + font-size: 11px; + } + + .btn-large { + padding: 8px 12px; + min-width: 32px; + height: 32px; + font-size: 12px; + } +} \ No newline at end of file diff --git a/web/src/apps/muse/base/table/types.ts b/web/src/apps/muse/base/table/types.ts new file mode 100644 index 0000000..30bf069 --- /dev/null +++ b/web/src/apps/muse/base/table/types.ts @@ -0,0 +1,74 @@ +import { Mark } from '../mock/collection'; + +// 表格列配置 +export interface TableColumn { + key: string; + title: string; + dataIndex: string; + width?: number; + render?: (value: any, record: T, index: number) => React.ReactNode; + sortable?: boolean; + fixed?: 'left' | 'right'; +} + +// 拖拽选择配置 +export interface DragSelectionConfig { + enabled?: boolean; // 是否启用拖拽选择,默认为 true + multi?: boolean; // 是否支持多选,默认为 true + onDragStart?: (startRowIndex: number) => void; // 拖拽开始回调 + onDragEnd?: (selectedRows: Mark[]) => void; // 拖拽结束回调 +} + +// 拖拽选择状态 +export interface DragSelectionState { + isDragging: boolean; + startPosition: { x: number; y: number } | null; + endPosition: { x: number; y: number } | null; + startRowIndex: number | null; + dragRect: DOMRect | null; + selectedDuringDrag: Set; +} + +// 表格行选择配置 +export interface RowSelection { + type?: 'checkbox' | 'radio'; + selectedRowKeys?: React.Key[]; + onChange?: (selectedRowKeys: React.Key[], selectedRows: T[]) => void; + getCheckboxProps?: (record: T) => { disabled?: boolean }; + dragSelection?: DragSelectionConfig; // 拖拽选择配置 +} + +// 虚拟滚动配置 +export interface VirtualScrollConfig { + rowHeight?: number; // 行高度,默认 48px + height?: number; // 表格容器高度,默认 400px +} + +// 表格操作按钮类型 +export interface ActionButton { + key: string; + label: string; + className?: string; + icon?: React.ReactNode; + onClick: (record: Mark) => void; + disabled?: (record: Mark) => boolean; + tooltip?: string; // 可选的自定义tooltip文本 + size?: 'small' | 'medium' | 'large'; // 按钮尺寸 +} + +// 表格属性 +export interface TableProps { + data: Mark[]; + columns: TableColumn[]; + loading?: boolean; + rowSelection?: RowSelection; + virtualScroll?: VirtualScrollConfig; + actions?: ActionButton[]; + onSort?: (field: string, order: 'asc' | 'desc' | null) => void; +} + +// 排序状态 +export interface SortState { + field: string | null; + order: 'asc' | 'desc' | null; +} \ No newline at end of file diff --git a/web/src/apps/muse/components/MarkDetail.css b/web/src/apps/muse/components/MarkDetail.css new file mode 100644 index 0000000..e46d3a1 --- /dev/null +++ b/web/src/apps/muse/components/MarkDetail.css @@ -0,0 +1,179 @@ +/* MarkDetail 组件样式 */ + +.mark-detail-container { + background: #ffffff; + border-radius: 8px; + padding: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + max-height: 200px; + overflow: hidden; + position: relative; +} + +.mark-detail-container.expanded { + max-height: none; + overflow: visible; +} + +.mark-detail-container h2 { + margin: 0 0 8px 0; + font-size: 18px; + font-weight: 600; + color: #333; + line-height: 1.4; +} + +.mark-detail-container p { + margin: 0 0 12px 0; + color: #666; + line-height: 1.5; +} + +/* 封面图片样式 */ +.mark-cover { + margin-bottom: 12px; + text-align: center; +} + +.mark-cover img { + max-width: 100%; + height: auto; + border-radius: 6px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +/* 摘要样式 */ +.mark-summary { + margin-bottom: 12px; + padding: 12px; + background-color: #f8f9fa; + border-radius: 6px; + border-left: 4px solid #007bff; +} + +.mark-summary h3 { + margin: 0 0 8px 0; + font-size: 14px; + font-weight: 600; + color: #333; +} + +.mark-summary p { + margin: 0; + font-size: 13px; + color: #555; + line-height: 1.4; +} + +/* 链接样式 */ +.mark-link { + margin-bottom: 12px; +} + +.mark-link a { + color: #007bff; + text-decoration: none; + padding: 6px 12px; + border: 1px solid #007bff; + border-radius: 4px; + display: inline-block; + font-size: 13px; + transition: all 0.2s ease; +} + +.mark-link a:hover { + background-color: #007bff; + color: white; +} + +/* 类型和日期样式 */ +.mark-type, +.mark-date { + margin-bottom: 8px; + font-size: 12px; + color: #666; +} + +.mark-type span, +.mark-date span { + background-color: #f1f3f4; + padding: 2px 6px; + border-radius: 3px; +} + +/* 标签样式 */ +.mark-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-bottom: 12px; +} + +.mark-tag { + background-color: #e3f2fd; + color: #1976d2; + padding: 4px 8px; + border-radius: 12px; + font-size: 11px; + font-weight: 500; +} + +/* 展开/收起按钮样式 */ +.mark-expand-button { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + margin-top: 12px; + padding: 8px 12px; + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 6px; + cursor: pointer; + font-size: 13px; + color: #495057; + transition: all 0.2s ease; + user-select: none; +} + +.mark-expand-button:hover { + background-color: #e9ecef; + border-color: #adb5bd; +} + +.mark-expand-button:active { + transform: translateY(1px); +} + +/* 收起状态下的渐变遮罩 */ +.mark-detail-container.collapsed::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 40px; + background: linear-gradient(transparent, white); + pointer-events: none; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .mark-detail-container { + padding: 12px; + } + + .mark-detail-container h2 { + font-size: 16px; + } + + .mark-detail-container p { + font-size: 14px; + } + + .mark-expand-button { + font-size: 12px; + padding: 6px 10px; + } +} \ No newline at end of file diff --git a/web/src/apps/muse/components/MarkDetal.tsx b/web/src/apps/muse/components/MarkDetal.tsx new file mode 100644 index 0000000..1ed23e1 --- /dev/null +++ b/web/src/apps/muse/components/MarkDetal.tsx @@ -0,0 +1,104 @@ +import React, { useState } from 'react'; +import { Maximize2, Minimize2 } from 'lucide-react'; +import './MarkDetail.css'; + +export type MarkShow = { + id: string; + title?: string; + description?: string; + tags?: string[]; + markType?: string; + cover?: string; + link?: string; + summary?: string; + key?: string; + data: any; + createdAt?: string; + updatedAt?: string; + markedAt?: Date; +} + +export type SimpleMarkShow = { + id: string; + title?: string; + description?: string; + tags?: string[]; + cover?: string; + link?: string; + summary?: string; +} + +interface MarkDetailProps { + data: MarkShow; +} + +export const MarkDetail: React.FC = ({ data }) => { + const [isExpanded, setIsExpanded] = useState(false); + const toggleExpanded = () => { + setIsExpanded(!isExpanded); + }; + + return ( +
+

{data.title}

+

{data.description}

+ + {data.cover && ( +
+ {data.title} +
+ )} + + {data.summary && isExpanded && ( +
+

摘要

+

{data.summary}

+
+ )} + + {data.link && isExpanded && ( + + )} + + {data.markType && isExpanded && ( +
+ 类型: {data.markType} +
+ )} + + {data.createdAt && isExpanded && ( +
+ 创建时间: {new Date(data.createdAt).toLocaleString()} +
+ )} + + {data.updatedAt && isExpanded && ( +
+ 更新时间: {new Date(data.updatedAt).toLocaleString()} +
+ )} + +
+ {data.tags?.map(tag => ( + {tag} + ))} +
+ +
+ {isExpanded ? ( + <> + + + ) : ( + <> + + + )} +
+
+ ) +} \ No newline at end of file diff --git a/web/src/apps/muse/index.tsx b/web/src/apps/muse/index.tsx new file mode 100644 index 0000000..61ec926 --- /dev/null +++ b/web/src/apps/muse/index.tsx @@ -0,0 +1,216 @@ +import { ToastContainer, toast } from 'react-toastify'; +import { AuthProvider } from '../login/AuthProvider'; +import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; +import { useState, useRef } from 'react'; + +import { App as Voice } from './videos/index.tsx'; +import { ChatInterface } from './prompts/index.tsx'; +import { BaseApp } from './base/index.tsx'; +import { exampleUsage, markService } from './modules/mark-service.ts'; + +const LeftPanel = () => { + return ( + +
+ +
+
+ ); +}; + +const CenterPanel = () => { + return ( + +
+ +
+
+ ); +}; + +const RightPanel = ({ isVisible }: { isVisible: boolean }) => { + if (!isVisible) return null; + + return ( + +
+

语音输入

+
+ +
+
+
+ ); +}; + +export const MuseApp = () => { + const [showRightPanel, setShowRightPanel] = useState(true); + const [showLeftPanel, setShowLeftPanel] = useState(true); + const [showCenterPanel, setShowCenterPanel] = useState(true); + const fileInputRef = useRef(null); + + // 导出数据库 + const handleExportDB = async () => { + try { + const filename = `marks_backup_${new Date().toISOString().split('T')[0]}.json`; + await markService.exportToFile(filename); + toast.success('数据库导出成功!'); + } catch (error) { + console.error('导出失败:', error); + toast.error('导出失败: ' + (error as Error).message); + } + }; + + // 触发导入文件选择 + const handleImportDB = () => { + fileInputRef.current?.click(); + }; + + // 处理文件选择并导入 + const handleFileImport = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + try { + const result = await markService.importFromFile(file); + toast.success( + `导入完成!成功: ${result.success}条,失败: ${result.failed}条,总计: ${result.total}条` + ); + } catch (error) { + console.error('导入失败:', error); + toast.error('导入失败: ' + (error as Error).message); + } + + // 重置文件输入 + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + // 删除数据库 + const handleDeleteDB = async () => { + if (!window.confirm('确定要删除所有数据吗?此操作无法撤销!')) { + return; + } + + try { + const success = await markService.clearDatabase(); + if (success) { + toast.success('数据库已清空!'); + } else { + toast.error('清空数据库失败!'); + } + } catch (error) { + console.error('删除失败:', error); + toast.error('删除失败: ' + (error as Error).message); + } + }; + + return ( +
+ {/* Panel Controls */} +
+
+ + + + + + + + {/* 隐藏的文件输入元素 */} + +
+
+ + {/* Resizable Panels */} +
+ + {showLeftPanel && } + + {showLeftPanel && showCenterPanel && ( + + )} + + {showCenterPanel && } + + {showCenterPanel && showRightPanel && ( + + )} + + {showRightPanel && } + +
+
+ ); +} + + +export const App: React.FC = () => { + return ( + + + + + ); +}; \ No newline at end of file diff --git a/web/src/apps/muse/modules/MarkModal.tsx b/web/src/apps/muse/modules/MarkModal.tsx new file mode 100644 index 0000000..dd7b9e2 --- /dev/null +++ b/web/src/apps/muse/modules/MarkModal.tsx @@ -0,0 +1,21 @@ +import ReactModal from 'react-modal' + +import { MarkDetail } from '../components/MarkDetal' + + +export const MarkModal = ({ isOpen, onRequestClose, markData }: { isOpen: boolean; onRequestClose: () => void; markData: any }) => { + return ( + + + + + ); +} \ No newline at end of file diff --git a/web/src/apps/muse/modules/db.ts b/web/src/apps/muse/modules/db.ts new file mode 100644 index 0000000..bfc4198 --- /dev/null +++ b/web/src/apps/muse/modules/db.ts @@ -0,0 +1,490 @@ +import PouchDB from 'pouchdb-browser'; +import { Mark, MarkEnsureType } from './mark'; + +console.log('PouchDB version:', PouchDB.version); +// 扩展 Mark 类型以包含 PouchDB 特有字段 +export type MarkDocument = Mark & { + _id: string; + _rev?: string; +}; + +// 创建或获取数据库实例 +export const createDB = (name: string = 'marks_db', opts?: { adapter?: string }) => { + return new PouchDB(name); +}; + +// 辅助函数:将 PouchDB 文档转换为 Mark 对象 +const docToMark = (doc: any): Mark => { + const { _id, _rev, ...mark } = doc as MarkDocument; + return mark; +}; + +// Mark 数据库操作类 +export class MarkDB { + private db: PouchDB.Database; + + constructor(dbName: string = 'marks_db') { + this.db = createDB(dbName); + } + + // 检查是否支持 find 方法 + private supportsFindAPI(): boolean { + return typeof this.db.find === 'function'; + } + + // 回退方案:使用 allDocs 过滤数据 + private async fallbackFind(filterFn: (doc: Mark) => boolean): Promise { + const allDocs = await this.getAll(); + return allDocs.filter(filterFn).sort((a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ); + } + + // 创建索引以支持查询 + async createIndexes() { + if (!this.db) { + throw new Error('数据库未初始化'); + } + + // 检查是否支持 createIndex 方法 (需要 pouchdb-find 插件) + if (typeof this.db.createIndex !== 'function') { + console.warn('PouchDB Find plugin not available. Skipping index creation.'); + console.warn('Some query features may not work optimally without indexes.'); + return; + } + + try { + // PouchDB 创建索引的正确方式 + const indexes = [ + { index: { fields: ['uid'] } }, + { index: { fields: ['markType'] } }, + { index: { fields: ['tags'] } }, + { index: { fields: ['createdAt'] } }, + { index: { fields: ['title'] } }, + { index: { fields: ['uid', 'markType'] } }, + { index: { fields: ['createdAt', 'uid'] } } + ]; + + const results = await Promise.allSettled( + indexes.map(indexDef => this.db.createIndex(indexDef)) + ); + + // 检查索引创建结果 + results.forEach((result, index) => { + if (result.status === 'fulfilled') { + console.log(`索引 ${index + 1} 创建成功:`, result.value); + } else { + // 如果索引已存在,PouchDB 会返回错误,这是正常的 + if (result.reason?.status !== 409) { + console.warn(`索引 ${index + 1} 创建失败:`, result.reason); + } + } + }); + + console.log('索引初始化完成'); + } catch (error) { + console.error('创建索引失败:', error); + // 不再抛出错误,而是警告用户 + console.warn('索引创建失败,但数据库可以继续使用(性能可能受影响)'); + } + } + + // 创建 Mark + async create(mark: Omit): Promise { + const now = new Date(); + const newMark: Mark = { + ...mark, + id: this.generateId(), + createdAt: now, + updatedAt: now + }; + + try { + const doc: MarkDocument = { + ...newMark, + _id: newMark.id + }; + const response = await this.db.put(doc); + return { ...newMark }; + } catch (error) { + console.error('创建 Mark 失败:', error); + throw error; + } + } + + // 根据 ID 获取 Mark + async getById(id: string): Promise { + try { + const doc = await this.db.get(id); + return docToMark(doc); + } catch (error: any) { + if (error.status === 404) { + return null; + } + console.error('获取 Mark 失败:', error); + throw error; + } + } + + // 获取所有 Marks + async getAll(): Promise { + try { + const result = await this.db.allDocs({ + include_docs: true, + attachments: false + }); + + return result.rows.map(row => docToMark(row.doc)); + } catch (error) { + console.error('获取所有 Marks 失败:', error); + throw error; + } + } + + // 按用户 ID 获取 Marks + async getByUserId(uid: string): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + uid: uid + }, + sort: [{ createdAt: 'desc' }] + }); + return result.docs.map(doc => docToMark(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((mark: Mark) => mark.uid === uid); + } + } catch (error) { + console.error('按用户获取 Marks 失败:', error); + throw error; + } + } + + // 按类型获取 Marks + async getByType(markType: MarkEnsureType): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + markType: markType + }, + sort: [{ createdAt: 'desc' }] + }); + return result.docs.map(doc => docToMark(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((mark: Mark) => mark.markType === markType); + } + } catch (error) { + console.error('按类型获取 Marks 失败:', error); + throw error; + } + } + + // 按标签搜索 Marks + async getByTag(tag: string): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + tags: { $elemMatch: { $eq: tag } } + }, + sort: [{ createdAt: 'desc' }] + }); + return result.docs.map(doc => docToMark(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((mark: Mark) => + Boolean(mark.tags && mark.tags.includes(tag)) + ); + } + } catch (error) { + console.error('按标签获取 Marks 失败:', error); + throw error; + } + } + + // 搜索 Marks(按标题或描述) + async search(query: string): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + $or: [ + { title: { $regex: query, $options: 'i' } }, + { description: { $regex: query, $options: 'i' } }, + { summary: { $regex: query, $options: 'i' } } + ] + }, + sort: [{ createdAt: 'desc' }] + }); + return result.docs.map(doc => docToMark(doc)); + } else { + // 回退方案:使用 allDocs 过滤,简单的字符串匹配 + const lowerQuery = query.toLowerCase(); + return await this.fallbackFind((mark: Mark) => { + const title = mark.title?.toLowerCase() || ''; + const description = mark.description?.toLowerCase() || ''; + const summary = mark.summary?.toLowerCase() || ''; + return title.includes(lowerQuery) || + description.includes(lowerQuery) || + summary.includes(lowerQuery); + }); + } + } catch (error) { + console.error('搜索 Marks 失败:', error); + throw error; + } + } + + // 更新 Mark + async update(id: string, updates: Partial>): Promise { + try { + const existingDoc = await this.db.get(id); + const existingMark = docToMark(existingDoc); + const updatedMark: Mark = { + ...existingMark, + ...updates, + updatedAt: new Date() + }; + + const doc: MarkDocument = { + ...updatedMark, + _id: id, + _rev: existingDoc._rev + }; + + await this.db.put(doc); + return updatedMark; + } catch (error) { + console.error('更新 Mark 失败:', error); + throw error; + } + } + + // 删除 Mark + async delete(id: string): Promise { + try { + const doc = await this.db.get(id); + await this.db.remove(doc); + return true; + } catch (error) { + console.error('删除 Mark 失败:', error); + throw error; + } + } + + // 批量删除 Marks + async deleteMultiple(ids: string[]): Promise { + try { + const docs = await Promise.all(ids.map(id => this.db.get(id))); + const responses = await Promise.all( + docs.map(doc => this.db.remove(doc)) + ); + return responses.every(response => response.ok); + } catch (error) { + console.error('批量删除 Marks 失败:', error); + throw error; + } + } + + // 分页获取 Marks + async getPaginated(page: number = 1, limit: number = 10, filters?: { + uid?: string; + markType?: MarkEnsureType; + tags?: string[]; + }): Promise<{ + marks: Mark[]; + total: number; + page: number; + limit: number; + totalPages: number; + }> { + try { + if (this.supportsFindAPI()) { + // 使用 find API + let selector: any = {}; + + if (filters?.uid) { + selector.uid = filters.uid; + } + + if (filters?.markType) { + selector.markType = filters.markType; + } + + if (filters?.tags && filters.tags.length > 0) { + selector.tags = { $elemMatch: { $in: filters.tags } }; + } + + // 获取总数 + const countResult = await this.db.find({ + selector, + fields: [] + }); + const total = countResult.docs.length; + + // 计算分页 + const skip = (page - 1) * limit; + const totalPages = Math.ceil(total / limit); + + // 获取数据 + const result = await this.db.find({ + selector, + sort: [{ createdAt: 'desc' }], + skip, + limit + }); + + return { + marks: result.docs.map(doc => docToMark(doc)), + total, + page, + limit, + totalPages + }; + } else { + // 回退方案:获取所有数据后在内存中分页 + let allMarks = await this.getAll(); + + // 应用过滤器 + if (filters) { + allMarks = allMarks.filter(mark => { + let matches = true; + + if (filters.uid && mark.uid !== filters.uid) { + matches = false; + } + + if (filters.markType && mark.markType !== filters.markType) { + matches = false; + } + + if (filters.tags && filters.tags.length > 0) { + const hasMatchingTag = filters.tags.some(tag => + mark.tags && mark.tags.includes(tag) + ); + if (!hasMatchingTag) { + matches = false; + } + } + + return matches; + }); + } + + const total = allMarks.length; + const totalPages = Math.ceil(total / limit); + const skip = (page - 1) * limit; + const marks = allMarks.slice(skip, skip + limit); + + return { + marks, + total, + page, + limit, + totalPages + }; + } + } catch (error) { + console.error('分页获取 Marks 失败:', error); + throw error; + } + } + + // 获取统计信息 + async getStats(): Promise<{ + total: number; + byType: Record; + byUser: Record; + recentActivity: number; + }> { + try { + const marks = await this.getAll(); + + const stats = { + total: marks.length, + byType: {} as Record, + byUser: {} as Record, + recentActivity: 0 + }; + + // 统计按类型分组 + marks.forEach(mark => { + if (mark.markType) { + stats.byType[mark.markType] = (stats.byType[mark.markType] || 0) + 1; + } + if (mark.uid) { + stats.byUser[mark.uid] = (stats.byUser[mark.uid] || 0) + 1; + } + }); + + // 统计最近7天的活动 + const weekAgo = new Date(); + weekAgo.setDate(weekAgo.getDate() - 7); + stats.recentActivity = marks.filter(mark => + new Date(mark.updatedAt) > weekAgo + ).length; + + return stats; + } catch (error) { + console.error('获取统计信息失败:', error); + throw error; + } + } + + // 同步数据库(用于远程同步) + async sync(remoteDB: string | PouchDB.Database): Promise { + try { + const remote = typeof remoteDB === 'string' ? new PouchDB(remoteDB) : remoteDB; + + await this.db.sync(remote).on('complete', () => { + console.log('同步完成'); + }).on('error', (err) => { + console.error('同步错误:', err); + }); + } catch (error) { + console.error('同步失败:', error); + throw error; + } + } + + // 清理数据库 + async clear(): Promise { + try { + const dbName = this.db.name; + await this.db.destroy(); + this.db = createDB(dbName); + await this.createIndexes(); + } catch (error) { + console.error('清理数据库失败:', error); + throw error; + } + } + + // 生成唯一ID + private generateId(): string { + return `mark_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; + } + + // 关闭数据库连接 + async close(): Promise { + try { + await this.db.close(); + } catch (error) { + console.error('关闭数据库失败:', error); + throw error; + } + } +} + +// 创建默认实例 +export const markDB = new MarkDB(); + +// 初始化数据库 +export const initMarkDB = async () => { + await markDB.createIndexes(); + return markDB; +}; \ No newline at end of file diff --git a/web/src/apps/muse/modules/mark-service.ts b/web/src/apps/muse/modules/mark-service.ts new file mode 100644 index 0000000..f4f8f42 --- /dev/null +++ b/web/src/apps/muse/modules/mark-service.ts @@ -0,0 +1,232 @@ +import { markDB, initMarkDB } from './db'; +import { Mark } from './mark'; + +// Mark 服务类 - 提供业务逻辑层 +export class MarkService { + private db = markDB; + + // 初始化服务 + async init() { + await initMarkDB(); + } + + // 创建新的 Mark + async createMark(markData: Omit): Promise { + return await this.db.create(markData); + } + + // 根据 ID 获取 Mark + async getMark(id: string): Promise { + return await this.db.getById(id); + } + + // 获取所有 Marks + async getAllMarks(): Promise { + return await this.db.getAll(); + } + + // 按用户获取 Marks + async getMarksByUser(userId: string): Promise { + return await this.db.getByUserId(userId); + } + + // 按类型获取 Marks + async getMarksByType(markType: string): Promise { + return await this.db.getByType(markType as any); + } + + // 按标签搜索 Marks + async getMarksByTag(tag: string): Promise { + return await this.db.getByTag(tag); + } + + // 搜索 Marks + async searchMarks(query: string): Promise { + return await this.db.search(query); + } + + // 更新 Mark + async updateMark(id: string, updates: Partial>): Promise { + return await this.db.update(id, updates); + } + + // 删除 Mark + async deleteMark(id: string): Promise { + return await this.db.delete(id); + } + + // 批量删除 Marks + async deleteMultipleMarks(ids: string[]): Promise { + return await this.db.deleteMultiple(ids); + } + + // 分页获取 Marks + async getMarksPaginated( + page: number = 1, + limit: number = 10, + filters?: { + uid?: string; + markType?: string; + tags?: string[]; + } + ) { + return await this.db.getPaginated(page, limit, filters); + } + + // 获取统计信息 + async getStats() { + return await this.db.getStats(); + } + + // 导出数据 + async exportData(): Promise { + return await this.getAllMarks(); + } + + // 导入数据 + async importData(marks: Mark[]): Promise { + let importedCount = 0; + try { + for (const mark of marks) { + const { id, createdAt, updatedAt, ...markData } = mark; + await this.createMark(markData); + importedCount++; + } + } catch (error) { + console.error('导入数据失败:', error); + } + return importedCount; + } + + // 导出数据到文件 + async exportToFile(filename: string = 'marks_backup.json'): Promise { + try { + const marks = await this.exportData(); + const exportData = { + version: '1.0', + exportTime: new Date().toISOString(), + totalCount: marks.length, + marks: marks + }; + + const jsonData = JSON.stringify(exportData, null, 2); + const blob = new Blob([jsonData], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + // 创建下载链接 + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + + console.log(`成功导出 ${marks.length} 条记录到文件: ${filename}`); + } catch (error) { + console.error('导出文件失败:', error); + throw new Error('导出文件失败: ' + (error as Error).message); + } + } + + // 从文件导入数据 + async importFromFile(file: File): Promise<{ success: number; failed: number; total: number }> { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = async (e) => { + try { + const content = e.target?.result as string; + const importData = JSON.parse(content); + + // 验证数据格式 + if (!importData.marks || !Array.isArray(importData.marks)) { + throw new Error('无效的数据格式:缺少marks数组'); + } + + let successCount = 0; + let failedCount = 0; + const totalCount = importData.marks.length; + + // 批量导入数据 + for (const mark of importData.marks) { + try { + // 移除ID相关字段,让系统重新生成 + const { id, createdAt, updatedAt, _id, _rev, ...markData } = mark; + await this.createMark(markData); + successCount++; + } catch (error) { + console.warn('导入单条记录失败:', error); + failedCount++; + } + } + + const result = { + success: successCount, + failed: failedCount, + total: totalCount + }; + + console.log(`导入完成: 成功${successCount}条,失败${failedCount}条,总计${totalCount}条`); + resolve(result); + } catch (error) { + console.error('解析导入文件失败:', error); + reject(new Error('解析导入文件失败: ' + (error as Error).message)); + } + }; + + reader.onerror = () => { + reject(new Error('读取文件失败')); + }; + + reader.readAsText(file); + }); + } + + // 清空数据库 + async clearDatabase(): Promise { + try { + await this.db.clear(); + console.log('数据库已清空'); + return true; + } catch (error) { + console.error('清空数据库失败:', error); + return false; + } + } +} + +// 创建默认服务实例 +export const markService = new MarkService(); + +// 使用示例函数 +export const exampleUsage = async () => { + // 1. 初始化服务 + await markService.init(); + + // 4. 获取所有标记 + const allMarks = await markService.getAllMarks(); + console.log('所有标记数量:', allMarks.length); + + // 5. 搜索标记 + const searchResults = await markService.searchMarks('测试'); + console.log('搜索结果数量:', searchResults.length); + + // 6. 按类型获取标记 + const markdownMarks = await markService.getMarksByType('markdown'); + console.log('Markdown 标记数量:', markdownMarks.length); + + // 7. 分页获取标记 + const paginatedResults = await markService.getMarksPaginated(1, 5); + console.log('分页结果:', { + currentPage: paginatedResults.page, + totalPages: paginatedResults.totalPages, + total: paginatedResults.total, + marksOnPage: paginatedResults.marks.length + }); + + // 8. 获取统计信息 + const stats = await markService.getStats(); + console.log('统计信息:', stats); + +}; \ No newline at end of file diff --git a/web/src/apps/muse/modules/mark.ts b/web/src/apps/muse/modules/mark.ts new file mode 100644 index 0000000..319192e --- /dev/null +++ b/web/src/apps/muse/modules/mark.ts @@ -0,0 +1,78 @@ +export type Mark = { + /** + * 标记ID + */ + id: string; + + /** + * 标题 + */ + title?: string; + + /** + * 描述 + */ + description?: string; + + /** + * 标签 + */ + tags?: string[]; + + /** + * 标记类型 + */ + markType?: string; + + /** + * 封面 + */ + cover?: string; + + /** + * 链接 + */ + link?: string; + + /** + * 摘要 + */ + summary?: string; + + /** + * 键 + */ + key?: string; + data: T; + + /** + * 附件列表 + */ + fileList?: any[]; + /** + * 创建人信息 + */ + uname?: string; + /** + * 版本号 + */ + version?: number; + /** + * 创建时间 + */ + createdAt: Date; + /** + * 更新时间 + */ + updatedAt: Date; + /** + * 标记时间 + */ + markedAt?: Date; + uid?: string; + puid?: string; +} + +const ensureType = ['markdown', 'json', 'html', 'image', 'video', 'audio', 'code', 'link', 'file'] + +export type MarkEnsureType = typeof ensureType[number]; diff --git a/web/src/apps/muse/modules/speak-db/README.md b/web/src/apps/muse/modules/speak-db/README.md new file mode 100644 index 0000000..2a31bb3 --- /dev/null +++ b/web/src/apps/muse/modules/speak-db/README.md @@ -0,0 +1,243 @@ +# Speak Database Service + +基于 PouchDB 的语音记录数据库服务,提供完整的增删改查功能。 + +## 功能特性 + +- 🎤 语音记录的完整 CRUD 操作 +- 📊 按天、说话人、类型等多维度查询 +- 🔍 全文搜索功能 +- 📈 统计分析功能 +- 🔄 数据导入导出 +- 📱 离线支持(基于 IndexedDB) +- 🔀 语音记录合并功能 + +## 快速开始 + +### 1. 初始化服务 + +```typescript +import { speakService } from './speak-db'; + +// 初始化服务 +await speakService.init(); +``` + +### 2. 创建语音记录 + +```typescript +// 自动创建(自动生成当天序号) +const speak = await speakService.createSpeakAuto({ + text: '这是识别的文字内容', + duration: 30, // 时长(秒) + speaker: 'user1', + type: 'normal' +}); + +// 手动指定所有字段 +const manualSpeak = await speakService.createSpeak({ + no: 1, + day: 365, + timestamp: new Date(), + text: '手动创建的语音记录', + duration: 45, + speaker: 'user2', + type: 'merge' +}); +``` + +### 3. 查询语音记录 + +```typescript +// 获取所有记录 +const allSpeaks = await speakService.getAllSpeaks(); + +// 获取今天的记录 +const todaySpeaks = await speakService.getTodaySpeaks(); + +// 按天查询 +const dayData = await speakService.getSpeaksByDay(365); + +// 按说话人查询 +const userSpeaks = await speakService.getSpeaksBySpeaker('user1'); + +// 按类型查询 +const normalSpeaks = await speakService.getSpeaksByType('normal'); + +// 时间范围查询 +const recentSpeaks = await speakService.getRecentSpeaks(7); // 最近7天 + +// 搜索文本内容 +const searchResults = await speakService.searchSpeaks('关键词'); + +// 分页查询 +const paginatedData = await speakService.getSpeaksPaginated(1, 10, { + speaker: 'user1', + type: 'normal' +}); +``` + +### 4. 更新和删除 + +```typescript +// 更新记录 +await speakService.updateSpeak('speak_id', { + text: '更新后的文字内容', + speaker: 'new_speaker' +}); + +// 删除单个记录 +await speakService.deleteSpeak('speak_id'); + +// 批量删除 +await speakService.deleteMultipleSpeaks(['id1', 'id2']); + +// 清空今天的记录 +await speakService.clearTodaySpeaks(); +``` + +### 5. 统计功能 + +```typescript +// 总体统计 +const stats = await speakService.getStats(); +console.log('总记录数:', stats.total); +console.log('总时长:', stats.totalDuration); +console.log('平均时长:', stats.avgDuration); + +// 今天的统计 +const todayStats = await speakService.getTodayStats(); +console.log('今天的记录数:', todayStats.total); +``` + +### 6. 数据导入导出 + +```typescript +// 导出到文件 +await speakService.exportToFile('speaks_backup.json'); + +// 从文件导入 +const fileInput = document.getElementById('fileInput') as HTMLInputElement; +const file = fileInput.files[0]; +const result = await speakService.importFromFile(file); +console.log(`导入完成: 成功${result.success}条,失败${result.failed}条`); +``` + +### 7. 高级功能 + +```typescript +// 合并多个语音记录 +const mergedSpeak = await speakService.mergeSpeaks( + ['speak1_id', 'speak2_id'], + { + text: '合并后的文字内容', + speaker: 'merged_speaker' + } +); + +// 获取短语音记录(小于5秒) +const shortSpeaks = await speakService.getShortSpeaks(5); + +// 获取长语音记录(大于60秒) +const longSpeaks = await speakService.getLongSpeaks(60); + +// 按时长范围查询 +const mediumSpeaks = await speakService.getSpeaksByDuration(10, 60); +``` + +## 数据结构 + +### Speak 类型 + +```typescript +type Speak = { + id: string; // 唯一标识 + no: number; // 当天序号 + file?: string; // base64编码的音频文件 + text?: string; // 识别的文字内容 + timestamp: Date; // 生成时间戳 + day: number; // 一年中的第几天 + duration: number; // 音频时长(秒) + speaker?: string; // 说话人 + type?: 'merge' | 'normal'; // 类型:合并或普通 + createdAt?: Date; // 创建时间 + updatedAt?: Date; // 更新时间 +} +``` + +### 过滤器选项 + +```typescript +interface SpeakFilters { + day?: number; // 按天过滤 + speaker?: string; // 按说话人过滤 + type?: 'merge' | 'normal'; // 按类型过滤 + startTime?: Date; // 开始时间 + endTime?: Date; // 结束时间 +} +``` + +## 索引和性能 + +数据库自动创建以下索引以优化查询性能: + +- `day` - 按天查询 +- `speaker` - 按说话人查询 +- `type` - 按类型查询 +- `timestamp` - 按时间查询 +- `day + no` - 复合索引,按天内序号查询 +- `timestamp + day` - 复合索引,时间和天的组合查询 + +## 注意事项 + +1. **时间处理**: 系统使用 `getDayOfYear()` 函数计算一年中的第几天 +2. **序号管理**: 使用 `createSpeakAuto()` 可以自动生成当天的序号 +3. **离线支持**: 基于 PouchDB,支持离线使用 +4. **数据同步**: 支持与远程数据库同步 +5. **错误处理**: 所有操作都包含完整的错误处理 + +## 依赖 + +- `pouchdb-browser`: PouchDB 的浏览器版本 +- `pouchdb-find`: 查询插件(可选,用于优化查询性能) + +## 示例应用 + +```typescript +import { speakService } from './speak-db'; + +class VoiceRecorderApp { + async init() { + await speakService.init(); + } + + async recordVoice(audioBlob: Blob, text: string) { + // 将音频转换为 base64 + const base64Audio = await this.blobToBase64(audioBlob); + + // 创建语音记录 + const speak = await speakService.createSpeakAuto({ + file: base64Audio, + text: text, + duration: audioBlob.size / 1000, // 估算时长 + speaker: 'current_user', + type: 'normal' + }); + + return speak; + } + + async getTodayRecordings() { + return await speakService.getTodaySpeaks(); + } + + private async blobToBase64(blob: Blob): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + } +} +``` \ No newline at end of file diff --git a/web/src/apps/muse/modules/speak-db/index.ts b/web/src/apps/muse/modules/speak-db/index.ts new file mode 100644 index 0000000..339d22e --- /dev/null +++ b/web/src/apps/muse/modules/speak-db/index.ts @@ -0,0 +1,26 @@ +// Speak 数据库和服务的统一导出文件 + +// 类型定义 +export { + Speak, + SpeakType, + CreateSpeakData, + UpdateSpeakData, + getDayOfYear +} from './speak'; + +// 数据库操作 +export { + SpeakDB, + SpeakDocument, + speakDB, + initSpeakDB, + createSpeakDB +} from './speak-db'; + +// 服务层 +export { + SpeakService, + speakService, + exampleUsage +} from './speak-service'; \ No newline at end of file diff --git a/web/src/apps/muse/modules/speak-db/speak-db.ts b/web/src/apps/muse/modules/speak-db/speak-db.ts new file mode 100644 index 0000000..0e6bed6 --- /dev/null +++ b/web/src/apps/muse/modules/speak-db/speak-db.ts @@ -0,0 +1,565 @@ +import PouchDB from 'pouchdb-browser'; +import { Speak } from './speak'; + +// 扩展 Speak 类型以包含 PouchDB 特有字段 +export type SpeakDocument = Speak & { + _id: string; + _rev?: string; +}; + +// 创建或获取数据库实例 +export const createSpeakDB = (name: string = 'speaks_db', opts?: { adapter?: string }) => { + return new PouchDB(name); +}; + +// 辅助函数:将 PouchDB 文档转换为 Speak 对象 +const docToSpeak = (doc: any): Speak => { + const { _id, _rev, ...speak } = doc as SpeakDocument; + return speak; +}; + +// Speak 数据库操作类 +export class SpeakDB { + private db: PouchDB.Database; + + constructor(dbName: string = 'speaks_db') { + this.db = createSpeakDB(dbName); + } + + // 检查是否支持 find 方法 + private supportsFindAPI(): boolean { + return typeof this.db.find === 'function'; + } + + // 回退方案:使用 allDocs 过滤数据 + private async fallbackFind(filterFn: (doc: Speak) => boolean): Promise { + const allDocs = await this.getAll(); + return allDocs.filter(filterFn).sort((a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ); + } + + // 创建索引以支持查询 + async createIndexes() { + if (!this.db) { + throw new Error('数据库未初始化'); + } + + // 检查是否支持 createIndex 方法 (需要 pouchdb-find 插件) + if (typeof this.db.createIndex !== 'function') { + console.warn('PouchDB Find plugin not available. Skipping index creation.'); + console.warn('Some query features may not work optimally without indexes.'); + return; + } + + try { + // PouchDB 创建索引的正确方式 + const indexes = [ + { index: { fields: ['day'] } }, + { index: { fields: ['no'] } }, + { index: { fields: ['timestamp'] } }, + { index: { fields: ['speaker'] } }, + { index: { fields: ['type'] } }, + { index: { fields: ['duration'] } }, + { index: { fields: ['day', 'no'] } }, + { index: { fields: ['timestamp', 'day'] } }, + { index: { fields: ['speaker', 'day'] } } + ]; + + const results = await Promise.allSettled( + indexes.map(indexDef => this.db.createIndex(indexDef)) + ); + + // 检查索引创建结果 + results.forEach((result, index) => { + if (result.status === 'fulfilled') { + console.log(`Speak索引 ${index + 1} 创建成功:`, result.value); + } else { + // 如果索引已存在,PouchDB 会返回错误,这是正常的 + if (result.reason?.status !== 409) { + console.warn(`Speak索引 ${index + 1} 创建失败:`, result.reason); + } + } + }); + + console.log('Speak索引初始化完成'); + } catch (error) { + console.error('创建Speak索引失败:', error); + // 不再抛出错误,而是警告用户 + console.warn('Speak索引创建失败,但数据库可以继续使用(性能可能受影响)'); + } + } + + // 创建 Speak + async create(speak: Omit): Promise { + const newSpeak: Speak = { + ...speak, + id: this.generateId() + }; + + try { + const doc: SpeakDocument = { + ...newSpeak, + _id: newSpeak.id + }; + const response = await this.db.put(doc); + return { ...newSpeak }; + } catch (error) { + console.error('创建 Speak 失败:', error); + throw error; + } + } + + // 根据 ID 获取 Speak + async getById(id: string): Promise { + try { + const doc = await this.db.get(id); + return docToSpeak(doc); + } catch (error: any) { + if (error.status === 404) { + return null; + } + console.error('获取 Speak 失败:', error); + throw error; + } + } + + // 获取所有 Speaks + async getAll(): Promise { + try { + const result = await this.db.allDocs({ + include_docs: true, + attachments: false + }); + + return result.rows + .map(row => docToSpeak(row.doc)) + .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); + } catch (error) { + console.error('获取所有 Speaks 失败:', error); + throw error; + } + } + + // 按天获取 Speaks + async getByDay(day: number): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + day: day + }, + sort: [{ no: 'asc' }] + }); + return result.docs.map(doc => docToSpeak(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((speak: Speak) => speak.day === day); + } + } catch (error) { + console.error('按天获取 Speaks 失败:', error); + throw error; + } + } + + // 按说话人获取 Speaks + async getBySpeaker(speaker: string): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + speaker: speaker + }, + sort: [{ timestamp: 'desc' }] + }); + return result.docs.map(doc => docToSpeak(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((speak: Speak) => speak.speaker === speaker); + } + } catch (error) { + console.error('按说话人获取 Speaks 失败:', error); + throw error; + } + } + + // 按类型获取 Speaks + async getByType(type: 'merge' | 'normal'): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + type: type + }, + sort: [{ timestamp: 'desc' }] + }); + return result.docs.map(doc => docToSpeak(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((speak: Speak) => speak.type === type); + } + } catch (error) { + console.error('按类型获取 Speaks 失败:', error); + throw error; + } + } + + // 按时间范围获取 Speaks + async getByTimeRange(startTime: Date, endTime: Date): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + timestamp: { + $gte: startTime, + $lte: endTime + } + }, + sort: [{ timestamp: 'desc' }] + }); + return result.docs.map(doc => docToSpeak(doc)); + } else { + // 回退方案:使用 allDocs 过滤 + return await this.fallbackFind((speak: Speak) => { + const speakTime = new Date(speak.timestamp); + return speakTime >= startTime && speakTime <= endTime; + }); + } + } catch (error) { + console.error('按时间范围获取 Speaks 失败:', error); + throw error; + } + } + + // 搜索 Speaks(按文字内容) + async search(query: string): Promise { + try { + if (this.supportsFindAPI()) { + const result = await this.db.find({ + selector: { + text: { $regex: query, $options: 'i' } + }, + sort: [{ timestamp: 'desc' }] + }); + return result.docs.map(doc => docToSpeak(doc)); + } else { + // 回退方案:使用 allDocs 过滤,简单的字符串匹配 + const lowerQuery = query.toLowerCase(); + return await this.fallbackFind((speak: Speak) => { + const text = speak.text?.toLowerCase() || ''; + return text.includes(lowerQuery); + }); + } + } catch (error) { + console.error('搜索 Speaks 失败:', error); + throw error; + } + } + + // 更新 Speak + async update(id: string, updates: Partial>): Promise { + try { + const existingDoc = await this.db.get(id); + const existingSpeak = docToSpeak(existingDoc); + const updatedSpeak: Speak = { + ...existingSpeak, + ...updates + }; + + const doc: SpeakDocument = { + ...updatedSpeak, + _id: id, + _rev: existingDoc._rev + }; + + await this.db.put(doc); + return updatedSpeak; + } catch (error) { + console.error('更新 Speak 失败:', error); + throw error; + } + } + + // 删除 Speak + async delete(id: string): Promise { + try { + const doc = await this.db.get(id); + await this.db.remove(doc); + return true; + } catch (error) { + console.error('删除 Speak 失败:', error); + throw error; + } + } + + // 批量删除 Speaks + async deleteMultiple(ids: string[]): Promise { + try { + const docs = await Promise.all(ids.map(id => this.db.get(id))); + const responses = await Promise.all( + docs.map(doc => this.db.remove(doc)) + ); + return responses.every(response => response.ok); + } catch (error) { + console.error('批量删除 Speaks 失败:', error); + throw error; + } + } + + // 按天删除 Speaks + async deleteByDay(day: number): Promise { + try { + const speaks = await this.getByDay(day); + const ids = speaks.map(speak => speak.id); + await this.deleteMultiple(ids); + return ids.length; + } catch (error) { + console.error('按天删除 Speaks 失败:', error); + throw error; + } + } + + // 分页获取 Speaks + async getPaginated(page: number = 1, limit: number = 10, filters?: { + day?: number; + speaker?: string; + type?: 'merge' | 'normal'; + startTime?: Date; + endTime?: Date; + }): Promise<{ + speaks: Speak[]; + total: number; + page: number; + limit: number; + totalPages: number; + }> { + try { + if (this.supportsFindAPI()) { + // 使用 find API + let selector: any = {}; + + if (filters?.day !== undefined) { + selector.day = filters.day; + } + + if (filters?.speaker) { + selector.speaker = filters.speaker; + } + + if (filters?.type) { + selector.type = filters.type; + } + + if (filters?.startTime || filters?.endTime) { + selector.timestamp = {}; + if (filters.startTime) { + selector.timestamp.$gte = filters.startTime; + } + if (filters.endTime) { + selector.timestamp.$lte = filters.endTime; + } + } + + // 获取总数 + const countResult = await this.db.find({ + selector, + fields: [] + }); + const total = countResult.docs.length; + + // 计算分页 + const skip = (page - 1) * limit; + const totalPages = Math.ceil(total / limit); + + // 获取数据 + const result = await this.db.find({ + selector, + sort: [{ timestamp: 'desc' }], + skip, + limit + }); + + return { + speaks: result.docs.map(doc => docToSpeak(doc)), + total, + page, + limit, + totalPages + }; + } else { + // 回退方案:获取所有数据后在内存中分页 + let allSpeaks = await this.getAll(); + + // 应用过滤器 + if (filters) { + allSpeaks = allSpeaks.filter(speak => { + let matches = true; + + if (filters.day !== undefined && speak.day !== filters.day) { + matches = false; + } + + if (filters.speaker && speak.speaker !== filters.speaker) { + matches = false; + } + + if (filters.type && speak.type !== filters.type) { + matches = false; + } + + if (filters.startTime || filters.endTime) { + const speakTime = new Date(speak.timestamp); + if (filters.startTime && speakTime < filters.startTime) { + matches = false; + } + if (filters.endTime && speakTime > filters.endTime) { + matches = false; + } + } + + return matches; + }); + } + + const total = allSpeaks.length; + const totalPages = Math.ceil(total / limit); + const skip = (page - 1) * limit; + const speaks = allSpeaks.slice(skip, skip + limit); + + return { + speaks, + total, + page, + limit, + totalPages + }; + } + } catch (error) { + console.error('分页获取 Speaks 失败:', error); + throw error; + } + } + + // 获取统计信息 + async getStats(): Promise<{ + total: number; + totalDuration: number; + avgDuration: number; + byDay: Record; + bySpeaker: Record; + byType: Record; + recentActivity: number; + }> { + try { + const speaks = await this.getAll(); + + const stats = { + total: speaks.length, + totalDuration: 0, + avgDuration: 0, + byDay: {} as Record, + bySpeaker: {} as Record, + byType: {} as Record, + recentActivity: 0 + }; + + // 统计总时长和各种分组 + speaks.forEach(speak => { + stats.totalDuration += speak.duration || 0; + + // 按天统计 + stats.byDay[speak.day] = (stats.byDay[speak.day] || 0) + 1; + + // 按说话人统计 + if (speak.speaker) { + stats.bySpeaker[speak.speaker] = (stats.bySpeaker[speak.speaker] || 0) + 1; + } + + // 按类型统计 + if (speak.type) { + stats.byType[speak.type] = (stats.byType[speak.type] || 0) + 1; + } + }); + + // 计算平均时长 + stats.avgDuration = stats.total > 0 ? stats.totalDuration / stats.total : 0; + + // 统计最近7天的活动 + const weekAgo = new Date(); + weekAgo.setDate(weekAgo.getDate() - 7); + stats.recentActivity = speaks.filter(speak => + new Date(speak.timestamp) > weekAgo + ).length; + + return stats; + } catch (error) { + console.error('获取Speak统计信息失败:', error); + throw error; + } + } + + // 获取当天的下一个序号 + async getNextNo(day: number): Promise { + try { + const todaySpeaks = await this.getByDay(day); + const maxNo = todaySpeaks.reduce((max, speak) => Math.max(max, speak.no), 0); + return maxNo + 1; + } catch (error) { + console.error('获取下一个序号失败:', error); + throw error; + } + } + + // 同步数据库(用于远程同步) + async sync(remoteDB: string | PouchDB.Database): Promise { + try { + const remote = typeof remoteDB === 'string' ? new PouchDB(remoteDB) : remoteDB; + + await this.db.sync(remote).on('complete', () => { + console.log('Speak同步完成'); + }).on('error', (err) => { + console.error('Speak同步错误:', err); + }); + } catch (error) { + console.error('Speak同步失败:', error); + throw error; + } + } + + // 清理数据库 + async clear(): Promise { + try { + const dbName = this.db.name; + await this.db.destroy(); + this.db = createSpeakDB(dbName); + await this.createIndexes(); + } catch (error) { + console.error('清理Speak数据库失败:', error); + throw error; + } + } + + // 生成唯一ID + private generateId(): string { + return `speak_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; + } + + // 关闭数据库连接 + async close(): Promise { + try { + await this.db.close(); + } catch (error) { + console.error('关闭Speak数据库失败:', error); + throw error; + } + } +} + +// 创建默认实例 +export const speakDB = new SpeakDB(); + +// 初始化数据库 +export const initSpeakDB = async () => { + await speakDB.createIndexes(); + return speakDB; +}; \ No newline at end of file diff --git a/web/src/apps/muse/modules/speak-db/speak-service.ts b/web/src/apps/muse/modules/speak-db/speak-service.ts new file mode 100644 index 0000000..cc564b9 --- /dev/null +++ b/web/src/apps/muse/modules/speak-db/speak-service.ts @@ -0,0 +1,399 @@ +import { speakDB, initSpeakDB } from './speak-db'; +import { Speak, CreateSpeakData, UpdateSpeakData, SpeakType, getDayOfYear } from './speak'; + +// Speak 服务类 - 提供业务逻辑层 +export class SpeakService { + private db = speakDB; + + // 初始化服务 + async init() { + await initSpeakDB(); + } + + // 创建新的 Speak + async createSpeak(speakData: CreateSpeakData): Promise { + // 自动设置创建和更新时间 + const now = new Date(); + const completeData = { + ...speakData, + createdAt: now, + updatedAt: now + }; + return await this.db.create(completeData); + } + + // 创建新的 Speak(自动获取当天序号) + async createSpeakAuto(speakData: Omit): Promise { + const today = new Date(); + const day = getDayOfYear(today); + const no = await this.db.getNextNo(day); + + return await this.createSpeak({ + ...speakData, + day, + no, + timestamp: today + }); + } + + // 根据 ID 获取 Speak + async getSpeak(id: string): Promise { + return await this.db.getById(id); + } + + // 获取所有 Speaks + async getAllSpeaks(): Promise { + return await this.db.getAll(); + } + + // 按天获取 Speaks + async getSpeaksByDay(day: number): Promise { + return await this.db.getByDay(day); + } + + // 获取今天的 Speaks + async getTodaySpeaks(): Promise { + const today = getDayOfYear(); + return await this.getSpeaksByDay(today); + } + + // 按说话人获取 Speaks + async getSpeaksBySpeaker(speaker: string): Promise { + return await this.db.getBySpeaker(speaker); + } + + // 按类型获取 Speaks + async getSpeaksByType(type: SpeakType): Promise { + return await this.db.getByType(type); + } + + // 按时间范围获取 Speaks + async getSpeaksByTimeRange(startTime: Date, endTime: Date): Promise { + return await this.db.getByTimeRange(startTime, endTime); + } + + // 获取最近几天的 Speaks + async getRecentSpeaks(days: number = 7): Promise { + const endTime = new Date(); + const startTime = new Date(); + startTime.setDate(startTime.getDate() - days); + + return await this.getSpeaksByTimeRange(startTime, endTime); + } + + // 搜索 Speaks + async searchSpeaks(query: string): Promise { + return await this.db.search(query); + } + + // 更新 Speak + async updateSpeak(id: string, updates: UpdateSpeakData): Promise { + // 自动设置更新时间 + const updatesWithTime = { + ...updates, + updatedAt: new Date() + }; + return await this.db.update(id, updatesWithTime); + } + + // 删除 Speak + async deleteSpeak(id: string): Promise { + return await this.db.delete(id); + } + + // 批量删除 Speaks + async deleteMultipleSpeaks(ids: string[]): Promise { + return await this.db.deleteMultiple(ids); + } + + // 按天删除 Speaks + async deleteSpeaksByDay(day: number): Promise { + return await this.db.deleteByDay(day); + } + + // 清空今天的 Speaks + async clearTodaySpeaks(): Promise { + const today = getDayOfYear(); + return await this.deleteSpeaksByDay(today); + } + + // 分页获取 Speaks + async getSpeaksPaginated( + page: number = 1, + limit: number = 10, + filters?: { + day?: number; + speaker?: string; + type?: SpeakType; + startTime?: Date; + endTime?: Date; + } + ) { + return await this.db.getPaginated(page, limit, filters); + } + + // 获取统计信息 + async getStats() { + return await this.db.getStats(); + } + + // 获取今天的统计信息 + async getTodayStats() { + const today = getDayOfYear(); + const todaySpeaks = await this.getSpeaksByDay(today); + + const stats = { + total: todaySpeaks.length, + totalDuration: todaySpeaks.reduce((sum, speak) => sum + (speak.duration || 0), 0), + avgDuration: 0, + bySpeaker: {} as Record, + byType: {} as Record + }; + + // 计算平均时长 + stats.avgDuration = stats.total > 0 ? stats.totalDuration / stats.total : 0; + + // 统计说话人和类型 + todaySpeaks.forEach(speak => { + if (speak.speaker) { + stats.bySpeaker[speak.speaker] = (stats.bySpeaker[speak.speaker] || 0) + 1; + } + if (speak.type) { + stats.byType[speak.type] = (stats.byType[speak.type] || 0) + 1; + } + }); + + return stats; + } + + // 导出数据 + async exportData(): Promise { + return await this.getAllSpeaks(); + } + + // 导入数据 + async importData(speaks: Speak[]): Promise { + let importedCount = 0; + try { + for (const speak of speaks) { + const { id, createdAt, updatedAt, ...speakData } = speak; + await this.createSpeak(speakData); + importedCount++; + } + } catch (error) { + console.error('导入Speak数据失败:', error); + } + return importedCount; + } + + // 导出数据到文件 + async exportToFile(filename: string = 'speaks_backup.json'): Promise { + try { + const speaks = await this.exportData(); + const exportData = { + version: '1.0', + exportTime: new Date().toISOString(), + totalCount: speaks.length, + speaks: speaks + }; + + const jsonData = JSON.stringify(exportData, null, 2); + const blob = new Blob([jsonData], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + // 创建下载链接 + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + + console.log(`成功导出 ${speaks.length} 条语音记录到文件: ${filename}`); + } catch (error) { + console.error('导出语音文件失败:', error); + throw new Error('导出语音文件失败: ' + (error as Error).message); + } + } + + // 从文件导入数据 + async importFromFile(file: File): Promise<{ success: number; failed: number; total: number }> { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = async (e) => { + try { + const content = e.target?.result as string; + const importData = JSON.parse(content); + + // 验证数据格式 + if (!importData.speaks || !Array.isArray(importData.speaks)) { + throw new Error('无效的数据格式:缺少speaks数组'); + } + + let successCount = 0; + let failedCount = 0; + const totalCount = importData.speaks.length; + + // 批量导入数据 + for (const speak of importData.speaks) { + try { + // 移除ID相关字段,让系统重新生成 + const { id, createdAt, updatedAt, _id, _rev, ...speakData } = speak; + await this.createSpeak(speakData); + successCount++; + } catch (error) { + console.warn('导入单条语音记录失败:', error); + failedCount++; + } + } + + const result = { + success: successCount, + failed: failedCount, + total: totalCount + }; + + console.log(`语音导入完成: 成功${successCount}条,失败${failedCount}条,总计${totalCount}条`); + resolve(result); + } catch (error) { + console.error('解析导入语音文件失败:', error); + reject(new Error('解析导入语音文件失败: ' + (error as Error).message)); + } + }; + + reader.onerror = () => { + reject(new Error('读取语音文件失败')); + }; + + reader.readAsText(file); + }); + } + + // 清空数据库 + async clearDatabase(): Promise { + try { + await this.db.clear(); + console.log('语音数据库已清空'); + return true; + } catch (error) { + console.error('清空语音数据库失败:', error); + return false; + } + } + + // 合并多个语音记录 + async mergeSpeaks(speakIds: string[], mergedData: { + text?: string; + speaker?: string; + duration?: number; + }): Promise { + try { + // 获取要合并的语音记录 + const speaks = await Promise.all( + speakIds.map(id => this.getSpeak(id)) + ); + + // 过滤掉不存在的记录 + const validSpeaks = speaks.filter(speak => speak !== null) as Speak[]; + + if (validSpeaks.length === 0) { + throw new Error('没有找到有效的语音记录进行合并'); + } + + // 计算合并后的数据 + const firstSpeak = validSpeaks[0]; + const totalDuration = validSpeaks.reduce((sum, speak) => sum + (speak.duration || 0), 0); + const combinedText = validSpeaks + .map(speak => speak.text || '') + .filter(text => text.length > 0) + .join(' '); + + // 创建合并后的记录 + const mergedSpeak = await this.createSpeakAuto({ + text: mergedData.text || combinedText, + speaker: mergedData.speaker || firstSpeak.speaker, + duration: mergedData.duration || totalDuration, + type: 'merge' + }); + + // 删除原始记录 + await this.deleteMultipleSpeaks(speakIds); + + return mergedSpeak; + } catch (error) { + console.error('合并语音记录失败:', error); + throw error; + } + } + + // 获取指定时长范围的语音记录 + async getSpeaksByDuration(minDuration: number, maxDuration?: number): Promise { + const allSpeaks = await this.getAllSpeaks(); + return allSpeaks.filter(speak => { + const duration = speak.duration || 0; + if (maxDuration !== undefined) { + return duration >= minDuration && duration <= maxDuration; + } + return duration >= minDuration; + }); + } + + // 获取短语音记录(小于指定时长) + async getShortSpeaks(maxDuration: number = 5): Promise { + return await this.getSpeaksByDuration(0, maxDuration); + } + + // 获取长语音记录(大于指定时长) + async getLongSpeaks(minDuration: number = 60): Promise { + return await this.getSpeaksByDuration(minDuration); + } +} + +// 创建默认服务实例 +export const speakService = new SpeakService(); + +// 使用示例函数 +export const exampleUsage = async () => { + // 1. 初始化服务 + await speakService.init(); + + // 2. 创建新的语音记录 + const newSpeak = await speakService.createSpeakAuto({ + text: '这是一个测试语音记录', + duration: 30, + speaker: 'user1', + type: 'normal' + }); + console.log('创建的语音记录:', newSpeak); + + // 3. 获取今天的语音记录 + const todaySpeaks = await speakService.getTodaySpeaks(); + console.log('今天的语音记录数量:', todaySpeaks.length); + + // 4. 搜索语音记录 + const searchResults = await speakService.searchSpeaks('测试'); + console.log('搜索结果数量:', searchResults.length); + + // 5. 按说话人获取记录 + const userSpeaks = await speakService.getSpeaksBySpeaker('user1'); + console.log('user1的语音记录数量:', userSpeaks.length); + + // 6. 分页获取记录 + const paginatedResults = await speakService.getSpeaksPaginated(1, 5); + console.log('分页结果:', { + currentPage: paginatedResults.page, + totalPages: paginatedResults.totalPages, + total: paginatedResults.total, + speaksOnPage: paginatedResults.speaks.length + }); + + // 7. 获取统计信息 + const stats = await speakService.getStats(); + console.log('统计信息:', stats); + + // 8. 获取今天的统计信息 + const todayStats = await speakService.getTodayStats(); + console.log('今天的统计信息:', todayStats); +}; \ No newline at end of file diff --git a/web/src/apps/muse/modules/speak-db/speak.ts b/web/src/apps/muse/modules/speak-db/speak.ts new file mode 100644 index 0000000..71ff868 --- /dev/null +++ b/web/src/apps/muse/modules/speak-db/speak.ts @@ -0,0 +1,32 @@ +// Speak 类型定义 +export type Speak = { + id: string; + no: number; // 序号, 当天的序号 + file?: string; // base64 编码的音频文件 + text?: string; // 文字内容,识别的内容 + timestamp: Date; // 生成时间戳 + day: number; // 365天中的第几天 + duration: number; // 音频时长,单位秒 + speaker?: string; // 说话人 + type?: 'merge' | 'normal'; // 语音类型,默认录制或者合并的 + createdAt?: Date; // 创建时间 + updatedAt?: Date; // 更新时间 +} + +// 语音类型枚举 +export type SpeakType = 'merge' | 'normal'; + +// 创建 Speak 时的数据类型(排除自动生成的字段) +export type CreateSpeakData = Omit; + +// 更新 Speak 时的数据类型 +export type UpdateSpeakData = Partial>; + +// 获取今天是一年中的第几天 +export function getDayOfYear(date: Date = new Date()): number { + const start = new Date(date.getFullYear(), 0, 0); + const diff = date.getTime() - start.getTime(); + const oneDay = 1000 * 60 * 60 * 24; + return Math.floor(diff / oneDay); +} + diff --git a/web/src/apps/muse/prompts/index.tsx b/web/src/apps/muse/prompts/index.tsx new file mode 100644 index 0000000..e06f834 --- /dev/null +++ b/web/src/apps/muse/prompts/index.tsx @@ -0,0 +1,190 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Send, Bot, User } from 'lucide-react'; + +interface Message { + id: string; + content: string; + role: 'user' | 'assistant'; + timestamp: Date; +} + +export const ChatInterface: React.FC = () => { + const [messages, setMessages] = useState([ + { + id: '1', + content: '你好!我是AI助手,有什么可以帮助您的吗?', + role: 'assistant', + timestamp: new Date() + } + ]); + const [inputValue, setInputValue] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const messagesEndRef = useRef(null); + const inputRef = useRef(null); + + // 自动滚动到最新消息 + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + // 发送消息 + const handleSend = async () => { + if (!inputValue.trim() || isLoading) return; + + const userMessage: Message = { + id: Date.now().toString(), + content: inputValue.trim(), + role: 'user', + timestamp: new Date() + }; + + setMessages(prev => [...prev, userMessage]); + setInputValue(''); + setIsLoading(true); + + // 模拟AI回复 + setTimeout(() => { + const aiMessage: Message = { + id: (Date.now() + 1).toString(), + content: `我收到了您的消息:"${userMessage.content}"。这里是我的回复,您还有其他问题吗?`, + role: 'assistant', + timestamp: new Date() + }; + setMessages(prev => [...prev, aiMessage]); + setIsLoading(false); + }, 1000); + }; + + // 处理键盘事件 + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + // 格式化时间 + const formatTime = (date: Date) => { + return date.toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit' + }); + }; + + return ( +
+ {/* 头部 */} +
+
+
+ +
+
+

智能工作台

+

在线 · 随时为您服务

+
+
+
+ + {/* 对话列表区域 */} +
+ {messages.map((message) => ( +
+ {message.role === 'assistant' && ( +
+ +
+ )} + +
+
+ {message.content} +
+
+ {formatTime(message.timestamp)} +
+
+ + {message.role === 'user' && ( +
+ +
+ )} +
+ ))} + + {/* 加载状态 */} + {isLoading && ( +
+
+ +
+
+
+
+
+
+
+
+
+ )} + +
+
+ + {/* 输入框区域 */} +
+
+
+