diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
new file mode 100644
index 0000000..02b16f4
--- /dev/null
+++ b/.gitea/workflows/ci.yml
@@ -0,0 +1,25 @@
+name: CI
+
+on:
+ push:
+ branches-ignore:
+ - master
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "25"
+
+ - name: Install deps
+ run: npm install
+
+ - name: Lint
+ run: npm run lint
+
+ - name: Build
+ run: npm run build
diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
new file mode 100644
index 0000000..acc815f
--- /dev/null
+++ b/.gitea/workflows/deploy.yml
@@ -0,0 +1,41 @@
+name: Deploy
+
+on:
+ push:
+ branches: [master]
+
+env:
+ REGISTRY: gitea.d3m0k1d.ru
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup SSH
+ run: |
+ mkdir -p ~/.ssh
+ echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
+ chmod 600 ~/.ssh/id_rsa
+
+ - name: Install Ansible
+ run: |
+ apt update && apt install -y ansible
+ ansible-galaxy install -r infra/ansible/requirements.yml
+
+ - name: Login to registry
+ run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login $REGISTRY -u "${{ secrets.REGISTRY_USER }}" --password-stdin
+
+ - name: Build and push
+ run: |
+ IMAGE=$REGISTRY/hellreign/frontend
+ docker build -f dockerfile -t $IMAGE:dev -t $IMAGE:latest .
+ docker push $IMAGE:dev
+ docker push $IMAGE:latest
+
+ - name: Deploy
+ run: |
+ echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > .vault_pass
+ ansible-playbook -i infra/ansible/inventory/hosts.yml infra/ansible/playbook.yml --vault-password-file .vault_pass
+ rm .vault_pass
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..768f283
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,7 @@
+services:
+ app:
+ build:
+ context: .
+ dockerfile: dockerfile
+ ports:
+ - "80:80"
diff --git a/dockerfile b/dockerfile
new file mode 100644
index 0000000..0e50b89
--- /dev/null
+++ b/dockerfile
@@ -0,0 +1,16 @@
+FROM node:25-alpine3.23 AS builder
+
+WORKDIR /app
+
+COPY package.json yarn.lock ./
+
+RUN yarn install --frozen-lockfile
+COPY . .
+RUN yarn build
+FROM nginx:alpine
+COPY --from=builder /app/dist /usr/share/nginx/html
+
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/infra/ansible/ansible.cfg b/infra/ansible/ansible.cfg
new file mode 100644
index 0000000..20f1d45
--- /dev/null
+++ b/infra/ansible/ansible.cfg
@@ -0,0 +1,7 @@
+[defaults]
+inventory = inventory/hosts.yml
+host_key_checking = False
+remote_user = root
+private_key_file = ~/.ssh/id_rsa
+interpreter_python = /usr/bin/python3
+stdout_callback = yaml
diff --git a/infra/ansible/inventory/hosts.yml b/infra/ansible/inventory/hosts.yml
new file mode 100644
index 0000000..c84a418
--- /dev/null
+++ b/infra/ansible/inventory/hosts.yml
@@ -0,0 +1,13 @@
+$ANSIBLE_VAULT;1.1;AES256
+63663666653739363337653532643363626133303030323462363762316364633838623636626636
+3163343137366530326139353638316466663037663935340a386362666236633237313939366639
+34626337346365663033386631366362366261366163646438646461376662666665363635396333
+3533626234383564390a663966376163366530643965306563363565326438313465383866343138
+66633432663430373339326365303033323133383365656231373736323234386435626431383639
+63396366333433343039343165633436633839666330646261633338666435353035656230313932
+33333630343535646338303539356532306632373433643536393537383463396330366634393962
+36356139616432336664613139623038373434643562353565353866303130323938383439396131
+30316139333733356462366464653964313264646632336566616536643438326433623363643465
+63343430373666356634323761363433666463366431343537613635363239636131643837353935
+64316633663334663536656137666330393034666661383165376365666633303764643439366461
+33386433643034643466
diff --git a/infra/ansible/playbook.yml b/infra/ansible/playbook.yml
new file mode 100644
index 0000000..db63d6d
--- /dev/null
+++ b/infra/ansible/playbook.yml
@@ -0,0 +1,47 @@
+---
+- name: Deploy Frontend
+ hosts: prod
+
+ pre_tasks:
+ - name: Install docker
+ ansible.builtin.include_role:
+ name: geerlingguy.docker
+
+ - name: Configure ufw
+ community.general.ufw:
+ rule: allow
+ port: "{{ item }}"
+ loop:
+ - "80"
+ - "443"
+ - "2222"
+
+ - name: Enable ufw
+ community.general.ufw:
+ state: enabled
+ tasks:
+ - name: Ensure directory
+ ansible.builtin.file:
+ path: /opt/aegisfront
+ state: directory
+
+ - name: Copy compose
+ ansible.builtin.copy:
+ src: "{{ playbook_dir }}/../docker-compose.yml"
+ dest: /opt/aegisfront/docker-compose.yml
+
+ - name: Pull image
+ ansible.builtin.shell:
+ cmd: docker compose pull
+ chdir: /opt/aegisfront
+ environment:
+ REGISTRY: gitea.d3m0k1d.ru
+ TAG: latest
+
+ - name: Start
+ ansible.builtin.shell:
+ cmd: docker compose up -d --remove-orphans
+ chdir: /opt/aegisfront
+ environment:
+ REGISTRY: gitea.d3m0k1d.ru
+ TAG: latest
diff --git a/infra/ansible/requirements.yml b/infra/ansible/requirements.yml
new file mode 100644
index 0000000..f643953
--- /dev/null
+++ b/infra/ansible/requirements.yml
@@ -0,0 +1,6 @@
+---
+roles:
+ - geerlingguy.docker
+
+collections:
+ - community.general
diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml
new file mode 100644
index 0000000..5530f18
--- /dev/null
+++ b/infra/docker-compose.yml
@@ -0,0 +1,5 @@
+services:
+ app:
+ image: ${REGISTRY}/hellreign/frontend:${TAG}
+ ports:
+ - "80:80"
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..66def58
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,31 @@
+server {
+ listen 80;
+ server_name localhost;
+
+ root /usr/share/nginx/html;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+ location /api/ {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_cache_bypass $http_upgrade;
+ }
+
+
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+
+ # Gzip сжатие
+ gzip on;
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
+}
diff --git a/package-lock.json b/package-lock.json
index 323cb31..36414ea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,8 +8,13 @@
"name": "app",
"version": "0.0.0",
"dependencies": {
+ "@tailwindcss/vite": "^4.3.0",
+ "axios": "^1.17.0",
"react": "^19.2.6",
- "react-dom": "^19.2.6"
+ "react-dom": "^19.2.6",
+ "react-icons": "^5.6.0",
+ "react-router-dom": "^7.17.0",
+ "tailwindcss": "^4.3.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
@@ -267,36 +272,10 @@
"node": ">=6.9.0"
}
},
- "node_modules/@emnapi/core": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
- "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@emnapi/wasi-threads": "1.2.1",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@emnapi/runtime": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
- "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
"node_modules/@emnapi/wasi-threads": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
- "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
- "dev": true,
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -501,7 +480,6 @@
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -512,7 +490,6 @@
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -523,7 +500,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -533,14 +509,12 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -551,7 +525,6 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
"integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
- "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -570,7 +543,6 @@
"version": "0.133.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
"integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
@@ -583,7 +555,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -600,7 +571,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -617,7 +587,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -634,7 +603,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -651,7 +619,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -668,7 +635,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -685,7 +651,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -702,7 +667,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -719,7 +683,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -736,7 +699,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -753,7 +715,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -770,7 +731,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -787,7 +747,6 @@
"cpu": [
"wasm32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -799,6 +758,37 @@
"node": "^20.19.0 || >=22.12.0"
}
},
+ "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
@@ -806,7 +796,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -823,7 +812,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -837,14 +825,269 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/@tailwindcss/node": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz",
+ "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.21.0",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.32.0",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.3.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz",
+ "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.3.0",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.0",
+ "@tailwindcss/oxide-darwin-x64": "4.3.0",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.0",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.0",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.0",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.0",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.0",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz",
+ "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz",
+ "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz",
+ "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz",
+ "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz",
+ "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz",
+ "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz",
+ "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz",
+ "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz",
+ "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz",
+ "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.10.0",
+ "@emnapi/runtime": "^1.10.0",
+ "@emnapi/wasi-threads": "^1.2.1",
+ "@napi-rs/wasm-runtime": "^1.1.4",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
+ "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz",
+ "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
+ "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.3.0",
+ "@tailwindcss/oxide": "4.3.0",
+ "tailwindcss": "4.3.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7 || ^8"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
- "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -876,7 +1119,7 @@
"version": "24.13.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
"integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -1198,6 +1441,18 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/ajv": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
@@ -1215,6 +1470,24 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz",
+ "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@@ -1286,6 +1559,19 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001799",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
@@ -1307,6 +1593,18 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -1314,6 +1612,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1340,7 +1651,6 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1361,16 +1671,38 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
- "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.371",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.371.tgz",
@@ -1378,6 +1710,64 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/enhanced-resolve": {
+ "version": "5.24.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz",
+ "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -1609,7 +1999,6 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -1674,11 +2063,46 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -1689,6 +2113,15 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -1699,6 +2132,43 @@
"node": ">=6.9.0"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -1725,6 +2195,63 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/hermes-estree": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
@@ -1742,6 +2269,19 @@
"hermes-estree": "0.25.1"
}
},
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -1792,6 +2332,15 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -1874,7 +2423,6 @@
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
- "dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
@@ -1907,7 +2455,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1928,7 +2475,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1949,7 +2495,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1970,7 +2515,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1991,7 +2535,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2012,7 +2555,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2033,7 +2575,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2054,7 +2595,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2075,7 +2615,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2096,7 +2635,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2117,7 +2655,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2157,6 +2694,45 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
@@ -2177,14 +2753,12 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2290,14 +2864,12 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
- "dev": true,
"license": "MIT",
"peer": true,
"engines": {
@@ -2311,7 +2883,6 @@
"version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2346,6 +2917,15 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -2371,6 +2951,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -2378,11 +2959,57 @@
"react": "^19.2.7"
}
},
+ "node_modules/react-icons": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
+ "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.17.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz",
+ "integrity": "sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.17.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.17.0.tgz",
+ "integrity": "sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.17.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
"node_modules/rolldown": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
"integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.133.0",
@@ -2428,6 +3055,12 @@
"semver": "bin/semver.js"
}
},
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -2455,17 +3088,34 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/tailwindcss": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
+ "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==",
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -2495,7 +3145,6 @@
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "dev": true,
"license": "0BSD",
"optional": true
},
@@ -2555,7 +3204,7 @@
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
@@ -2603,7 +3252,6 @@
"version": "8.0.16",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
- "dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
diff --git a/package.json b/package.json
index 44698a5..501b254 100644
--- a/package.json
+++ b/package.json
@@ -5,13 +5,19 @@
"type": "module",
"scripts": {
"dev": "vite",
- "build": "tsc -b && vite build",
+ "build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
+ "@tailwindcss/vite": "^4.3.0",
+ "axios": "^1.17.0",
+ "lucide-react": "^1.18.0",
"react": "^19.2.6",
- "react-dom": "^19.2.6"
+ "react-dom": "^19.2.6",
+ "react-icons": "^5.6.0",
+ "react-router-dom": "^7.17.0",
+ "tailwindcss": "^4.3.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
diff --git a/src/App.css b/src/App.css
deleted file mode 100644
index f90339d..0000000
--- a/src/App.css
+++ /dev/null
@@ -1,184 +0,0 @@
-.counter {
- font-size: 16px;
- padding: 5px 10px;
- border-radius: 5px;
- color: var(--accent);
- background: var(--accent-bg);
- border: 2px solid transparent;
- transition: border-color 0.3s;
- margin-bottom: 24px;
-
- &:hover {
- border-color: var(--accent-border);
- }
- &:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 2px;
- }
-}
-
-.hero {
- position: relative;
-
- .base,
- .framework,
- .vite {
- inset-inline: 0;
- margin: 0 auto;
- }
-
- .base {
- width: 170px;
- position: relative;
- z-index: 0;
- }
-
- .framework,
- .vite {
- position: absolute;
- }
-
- .framework {
- z-index: 1;
- top: 34px;
- height: 28px;
- transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
- scale(1.4);
- }
-
- .vite {
- z-index: 0;
- top: 107px;
- height: 26px;
- width: auto;
- transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
- scale(0.8);
- }
-}
-
-#center {
- display: flex;
- flex-direction: column;
- gap: 25px;
- place-content: center;
- place-items: center;
- flex-grow: 1;
-
- @media (max-width: 1024px) {
- padding: 32px 20px 24px;
- gap: 18px;
- }
-}
-
-#next-steps {
- display: flex;
- border-top: 1px solid var(--border);
- text-align: left;
-
- & > div {
- flex: 1 1 0;
- padding: 32px;
- @media (max-width: 1024px) {
- padding: 24px 20px;
- }
- }
-
- .icon {
- margin-bottom: 16px;
- width: 22px;
- height: 22px;
- }
-
- @media (max-width: 1024px) {
- flex-direction: column;
- text-align: center;
- }
-}
-
-#docs {
- border-right: 1px solid var(--border);
-
- @media (max-width: 1024px) {
- border-right: none;
- border-bottom: 1px solid var(--border);
- }
-}
-
-#next-steps ul {
- list-style: none;
- padding: 0;
- display: flex;
- gap: 8px;
- margin: 32px 0 0;
-
- .logo {
- height: 18px;
- }
-
- a {
- color: var(--text-h);
- font-size: 16px;
- border-radius: 6px;
- background: var(--social-bg);
- display: flex;
- padding: 6px 12px;
- align-items: center;
- gap: 8px;
- text-decoration: none;
- transition: box-shadow 0.3s;
-
- &:hover {
- box-shadow: var(--shadow);
- }
- .button-icon {
- height: 18px;
- width: 18px;
- }
- }
-
- @media (max-width: 1024px) {
- margin-top: 20px;
- flex-wrap: wrap;
- justify-content: center;
-
- li {
- flex: 1 1 calc(50% - 8px);
- }
-
- a {
- width: 100%;
- justify-content: center;
- box-sizing: border-box;
- }
- }
-}
-
-#spacer {
- height: 88px;
- border-top: 1px solid var(--border);
- @media (max-width: 1024px) {
- height: 48px;
- }
-}
-
-.ticks {
- position: relative;
- width: 100%;
-
- &::before,
- &::after {
- content: '';
- position: absolute;
- top: -4.5px;
- border: 5px solid transparent;
- }
-
- &::before {
- left: 0;
- border-left-color: var(--border);
- }
- &::after {
- right: 0;
- border-right-color: var(--border);
- }
-}
diff --git a/src/App.tsx b/src/App.tsx
deleted file mode 100644
index a66b5ef..0000000
--- a/src/App.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from './assets/vite.svg'
-import heroImg from './assets/hero.png'
-import './App.css'
-
-function App() {
- const [count, setCount] = useState(0)
-
- return (
- <>
-
-
-
-
Get started
-
- Edit src/App.tsx and save to test HMR
-
-
-
-
-
-
-
-
-
-
-
Documentation
-
Your questions, answered
-
-
-
-
-
Connect with us
-
Join the Vite community
-
-
-
-
-
-
- >
- )
-}
-
-export default App
diff --git a/src/app/App.tsx b/src/app/App.tsx
new file mode 100644
index 0000000..9a228b3
--- /dev/null
+++ b/src/app/App.tsx
@@ -0,0 +1,12 @@
+import { Routing } from "./providers/routing";
+import "@/shared/styles/index.css";
+
+function App() {
+ return (
+ <>
+
+ >
+ );
+}
+
+export default App;
diff --git a/src/app/providers/helper/protected.route.tsx b/src/app/providers/helper/protected.route.tsx
new file mode 100644
index 0000000..b702b45
--- /dev/null
+++ b/src/app/providers/helper/protected.route.tsx
@@ -0,0 +1,20 @@
+import { Navigate } from "react-router-dom";
+import { authService } from "@/modules/auth/api/auth.service";
+
+interface ProtectedRouteProps {
+ children: React.ReactNode;
+ fallbackPath?: string;
+}
+
+export const ProtectedRoute: React.FC = ({
+ children,
+ fallbackPath = "/auth",
+}) => {
+ const isAuthenticated = authService.isAuthenticated();
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ return <>{children}>;
+};
diff --git a/src/app/providers/routing.tsx b/src/app/providers/routing.tsx
new file mode 100644
index 0000000..132830c
--- /dev/null
+++ b/src/app/providers/routing.tsx
@@ -0,0 +1,49 @@
+import { Suspense } from "react";
+import { Routes as ReactRoutes, Route } from "react-router-dom";
+import { HomePage } from "@/pages/home.page";
+import { SecondaryPage } from "@/pages/secondary.page";
+import { AuthPage } from "@/pages/AuthPage";
+import { CreateOrganizationPage } from "@/pages/CreateOrganizationPage";
+import { OrganizationPage } from "@/pages/OrganizationPage";
+import { ProtectedRoute } from "./helper/protected.route";
+
+export const Routing = () => {
+ return (
+
+ Загрузка...
+
+ }
+ >
+
+ } />
+ } />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+ );
+};
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index 5fb3313..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,111 +0,0 @@
-:root {
- --text: #6b6375;
- --text-h: #08060d;
- --bg: #fff;
- --border: #e5e4e7;
- --code-bg: #f4f3ec;
- --accent: #aa3bff;
- --accent-bg: rgba(170, 59, 255, 0.1);
- --accent-border: rgba(170, 59, 255, 0.5);
- --social-bg: rgba(244, 243, 236, 0.5);
- --shadow:
- rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
-
- --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
- --heading: system-ui, 'Segoe UI', Roboto, sans-serif;
- --mono: ui-monospace, Consolas, monospace;
-
- font: 18px/145% var(--sans);
- letter-spacing: 0.18px;
- color-scheme: light dark;
- color: var(--text);
- background: var(--bg);
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-
- @media (max-width: 1024px) {
- font-size: 16px;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --text: #9ca3af;
- --text-h: #f3f4f6;
- --bg: #16171d;
- --border: #2e303a;
- --code-bg: #1f2028;
- --accent: #c084fc;
- --accent-bg: rgba(192, 132, 252, 0.15);
- --accent-border: rgba(192, 132, 252, 0.5);
- --social-bg: rgba(47, 48, 58, 0.5);
- --shadow:
- rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
- }
-
- #social .button-icon {
- filter: invert(1) brightness(2);
- }
-}
-
-#root {
- width: 1126px;
- max-width: 100%;
- margin: 0 auto;
- text-align: center;
- border-inline: 1px solid var(--border);
- min-height: 100svh;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
-}
-
-body {
- margin: 0;
-}
-
-h1,
-h2 {
- font-family: var(--heading);
- font-weight: 500;
- color: var(--text-h);
-}
-
-h1 {
- font-size: 56px;
- letter-spacing: -1.68px;
- margin: 32px 0;
- @media (max-width: 1024px) {
- font-size: 36px;
- margin: 20px 0;
- }
-}
-h2 {
- font-size: 24px;
- line-height: 118%;
- letter-spacing: -0.24px;
- margin: 0 0 8px;
- @media (max-width: 1024px) {
- font-size: 20px;
- }
-}
-p {
- margin: 0;
-}
-
-code,
-.counter {
- font-family: var(--mono);
- display: inline-flex;
- border-radius: 4px;
- color: var(--text-h);
-}
-
-code {
- font-size: 15px;
- line-height: 135%;
- padding: 4px 8px;
- background: var(--code-bg);
-}
diff --git a/src/main.tsx b/src/main.tsx
index bef5202..5f5373c 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,10 +1,12 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.tsx'
+import { createRoot } from "react-dom/client";
+import { BrowserRouter } from "react-router-dom";
+import App from "./app/App.tsx";
+import { ThemeInitialProvider } from "@/modules/theme-changer";
-createRoot(document.getElementById('root')!).render(
-
-
- ,
-)
+createRoot(document.getElementById("root")!).render(
+
+
+
+
+ ,
+);
diff --git a/src/modules/auth/api/auth.service.ts b/src/modules/auth/api/auth.service.ts
new file mode 100644
index 0000000..5000b83
--- /dev/null
+++ b/src/modules/auth/api/auth.service.ts
@@ -0,0 +1,394 @@
+import type {
+ LoginCredentials,
+ RegisterData,
+ AuthResponse,
+ OrganizationCreateData,
+ OrganizationResponse,
+ OrganizationMember,
+} from "../types/auth.types";
+
+// Заглушка для хранения данных в localStorage
+const MOCK_USERS_KEY = "mock_users";
+const MOCK_ORGS_KEY = "mock_organizations";
+const MOCK_MEMBERS_KEY = "mock_members";
+
+const getMockUsers = () => {
+ const users = localStorage.getItem(MOCK_USERS_KEY);
+ if (!users) {
+ const defaultUsers = [
+ {
+ id: 1,
+ username: "admin",
+ email: "admin@example.com",
+ password: "admin123",
+ first_name: "Admin",
+ last_name: "User",
+ },
+ ];
+ localStorage.setItem(MOCK_USERS_KEY, JSON.stringify(defaultUsers));
+ return defaultUsers;
+ }
+ return JSON.parse(users);
+};
+
+const getMockOrganizations = () => {
+ const orgs = localStorage.getItem(MOCK_ORGS_KEY);
+ if (!orgs) {
+ return [];
+ }
+ return JSON.parse(orgs);
+};
+
+const getMockMembers = () => {
+ const members = localStorage.getItem(MOCK_MEMBERS_KEY);
+ if (!members) {
+ return [];
+ }
+ return JSON.parse(members);
+};
+
+export const authService = {
+ async login(credentials: LoginCredentials): Promise {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ const users = getMockUsers();
+ const user = users.find(
+ (u: any) =>
+ u.username === credentials.username &&
+ u.password === credentials.password,
+ );
+
+ if (user) {
+ resolve({
+ access_token: `mock_token_${user.id}_${Date.now()}`,
+ token_type: "bearer",
+ user: {
+ id: user.id,
+ username: user.username,
+ email: user.email,
+ first_name: user.first_name,
+ last_name: user.last_name,
+ },
+ });
+ } else {
+ reject({
+ response: {
+ data: { message: "Неверное имя пользователя или пароль" },
+ },
+ });
+ }
+ }, 500);
+ });
+ },
+
+ async register(
+ data: Omit,
+ ): Promise {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ const users = getMockUsers();
+ const existingUser = users.find(
+ (u: any) => u.username === data.username,
+ );
+
+ if (existingUser) {
+ reject({
+ response: {
+ data: { message: "Пользователь с таким именем уже существует" },
+ },
+ });
+ return;
+ }
+
+ const existingEmail = users.find((u: any) => u.email === data.email);
+ if (existingEmail) {
+ reject({
+ response: {
+ data: { message: "Пользователь с таким email уже существует" },
+ },
+ });
+ return;
+ }
+
+ const newUser = {
+ id: users.length + 1,
+ username: data.username,
+ email: data.email,
+ password: data.password,
+ first_name: data.first_name,
+ last_name: data.last_name,
+ };
+
+ users.push(newUser);
+ localStorage.setItem(MOCK_USERS_KEY, JSON.stringify(users));
+
+ resolve({
+ access_token: `mock_token_${newUser.id}_${Date.now()}`,
+ token_type: "bearer",
+ user: {
+ id: newUser.id,
+ username: newUser.username,
+ email: newUser.email,
+ first_name: newUser.first_name,
+ last_name: newUser.last_name,
+ },
+ });
+ }, 500);
+ });
+ },
+
+ async createOrganization(
+ data: OrganizationCreateData,
+ ): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const orgs = getMockOrganizations();
+ const token = this.getToken();
+ const userId = token ? parseInt(token.split("_")[1]) : 1;
+
+ const newOrg = {
+ id: orgs.length + 1,
+ name: data.name,
+ logo_url: data.logo ? URL.createObjectURL(data.logo) : undefined,
+ created_at: new Date().toISOString(),
+ owner_id: userId,
+ };
+
+ orgs.push(newOrg);
+ localStorage.setItem(MOCK_ORGS_KEY, JSON.stringify(orgs));
+
+ // Добавляем владельца в члены организации
+ const members = getMockMembers();
+ const newMember = {
+ id: members.length + 1,
+ organization_id: newOrg.id,
+ user_id: userId,
+ username: "temp_username",
+ email: "temp_email",
+ first_name: "temp_first",
+ last_name: "temp_last",
+ role: "owner",
+ joined_at: new Date().toISOString(),
+ };
+
+ // Получаем данные пользователя
+ const users = getMockUsers();
+ const user = users.find((u: any) => u.id === userId);
+ if (user) {
+ newMember.username = user.username;
+ newMember.email = user.email;
+ newMember.first_name = user.first_name;
+ newMember.last_name = user.last_name;
+ }
+
+ members.push(newMember);
+ localStorage.setItem(MOCK_MEMBERS_KEY, JSON.stringify(members));
+
+ resolve(newOrg);
+ }, 500);
+ });
+ },
+
+ async getOrganizations(): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const orgs = getMockOrganizations();
+ resolve(orgs);
+ }, 300);
+ });
+ },
+
+ async getOrganizationMembers(
+ organizationId: number,
+ ): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const members = getMockMembers();
+ const orgMembers = members.filter(
+ (m: any) => m.organization_id === organizationId,
+ );
+ resolve(orgMembers);
+ }, 300);
+ });
+ },
+
+ async updateMemberRole(
+ organizationId: number,
+ userId: number,
+ newRole: string,
+ ): Promise {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ const members = getMockMembers();
+ const memberIndex = members.findIndex(
+ (m: any) =>
+ m.organization_id === organizationId && m.user_id === userId,
+ );
+
+ if (memberIndex !== -1) {
+ members[memberIndex].role = newRole;
+ localStorage.setItem(MOCK_MEMBERS_KEY, JSON.stringify(members));
+ resolve();
+ } else {
+ reject({ response: { data: { message: "Участник не найден" } } });
+ }
+ }, 500);
+ });
+ },
+
+ async removeMember(organizationId: number, userId: number): Promise {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ const members = getMockMembers();
+ const filteredMembers = members.filter(
+ (m: any) =>
+ !(m.organization_id === organizationId && m.user_id === userId),
+ );
+
+ if (filteredMembers.length !== members.length) {
+ localStorage.setItem(
+ MOCK_MEMBERS_KEY,
+ JSON.stringify(filteredMembers),
+ );
+ resolve();
+ } else {
+ reject({ response: { data: { message: "Участник не найден" } } });
+ }
+ }, 500);
+ });
+ },
+
+ async addMember(
+ organizationId: number,
+ email: string,
+ role: string,
+ ): Promise {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ const users = getMockUsers();
+ const user = users.find((u: any) => u.email === email);
+
+ if (!user) {
+ reject({
+ response: {
+ data: { message: "Пользователь с таким email не найден" },
+ },
+ });
+ return;
+ }
+
+ const members = getMockMembers();
+ const existingMember = members.find(
+ (m: any) =>
+ m.organization_id === organizationId && m.user_id === user.id,
+ );
+
+ if (existingMember) {
+ reject({
+ response: {
+ data: {
+ message: "Пользователь уже является участником организации",
+ },
+ },
+ });
+ return;
+ }
+
+ const newMember = {
+ id: members.length + 1,
+ organization_id: organizationId,
+ user_id: user.id,
+ username: user.username,
+ email: user.email,
+ first_name: user.first_name,
+ last_name: user.last_name,
+ role: role,
+ joined_at: new Date().toISOString(),
+ };
+
+ members.push(newMember);
+ localStorage.setItem(MOCK_MEMBERS_KEY, JSON.stringify(members));
+
+ resolve(newMember);
+ }, 500);
+ });
+ },
+
+ async getCurrentUser(): Promise {
+ return new Promise((resolve, reject) => {
+ const token = this.getToken();
+ if (!token) {
+ reject({ response: { data: { message: "Не авторизован" } } });
+ return;
+ }
+
+ const userId = parseInt(token.split("_")[1]);
+ const users = getMockUsers();
+ const user = users.find((u: any) => u.id === userId);
+
+ if (user) {
+ resolve({
+ id: user.id,
+ username: user.username,
+ email: user.email,
+ first_name: user.first_name,
+ last_name: user.last_name,
+ });
+ } else {
+ reject({ response: { data: { message: "Пользователь не найден" } } });
+ }
+ });
+ },
+
+ logout(): void {
+ localStorage.removeItem("auth-storage");
+ localStorage.removeItem("organization-storage");
+ },
+
+ saveAuthData(token: string, user: AuthResponse["user"]): void {
+ const authStorage = {
+ state: {
+ token,
+ user,
+ },
+ version: 0,
+ };
+ localStorage.setItem("auth-storage", JSON.stringify(authStorage));
+ },
+
+ saveOrganization(organization: OrganizationResponse): void {
+ localStorage.setItem("organization-storage", JSON.stringify(organization));
+ },
+
+ getToken(): string | null {
+ const authStorage = localStorage.getItem("auth-storage");
+ if (!authStorage) return null;
+
+ try {
+ const parsed = JSON.parse(authStorage);
+ return parsed.state?.token || null;
+ } catch {
+ return null;
+ }
+ },
+
+ getCurrentOrganization(): OrganizationResponse | null {
+ const orgStorage = localStorage.getItem("organization-storage");
+ if (!orgStorage) return null;
+
+ try {
+ return JSON.parse(orgStorage);
+ } catch {
+ return null;
+ }
+ },
+
+ isAuthenticated(): boolean {
+ const token = this.getToken();
+ return !!token;
+ },
+
+ hasOrganization(): boolean {
+ return !!this.getCurrentOrganization();
+ },
+};
diff --git a/src/modules/auth/components/CreateOrganizationForm.tsx b/src/modules/auth/components/CreateOrganizationForm.tsx
new file mode 100644
index 0000000..4477f39
--- /dev/null
+++ b/src/modules/auth/components/CreateOrganizationForm.tsx
@@ -0,0 +1,138 @@
+import React, { useState, useRef } from "react";
+import { useAuth } from "../hooks/useAuth";
+
+export const CreateOrganizationForm = () => {
+ const [organizationName, setOrganizationName] = useState("");
+ const [logo, setLogo] = useState(null);
+ const [logoPreview, setLogoPreview] = useState("");
+ const fileInputRef = useRef(null);
+ const { createOrganization, isLoading, error } = useAuth();
+
+ const handleLogoChange = (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (file) {
+ setLogo(file);
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ setLogoPreview(reader.result as string);
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (organizationName.trim()) {
+ await createOrganization({
+ name: organizationName,
+ logo: logo || undefined,
+ });
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/src/modules/auth/components/LoginForm.tsx b/src/modules/auth/components/LoginForm.tsx
new file mode 100644
index 0000000..9a14f78
--- /dev/null
+++ b/src/modules/auth/components/LoginForm.tsx
@@ -0,0 +1,109 @@
+import React, { useState } from "react";
+import { useAuth } from "../hooks/useAuth";
+
+interface LoginFormProps {
+ onSwitchToRegister: () => void;
+}
+
+export const LoginForm: React.FC = ({ onSwitchToRegister }) => {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const { login, isLoading, error } = useAuth();
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ await login({ username, password });
+ };
+
+ return (
+
+ );
+};
diff --git a/src/modules/auth/components/RegisterForm.tsx b/src/modules/auth/components/RegisterForm.tsx
new file mode 100644
index 0000000..7033db6
--- /dev/null
+++ b/src/modules/auth/components/RegisterForm.tsx
@@ -0,0 +1,231 @@
+import React, { useState } from "react";
+import { useAuth } from "../hooks/useAuth";
+
+interface RegisterFormProps {
+ onSwitchToLogin: () => void;
+}
+
+export const RegisterForm: React.FC = ({
+ onSwitchToLogin,
+}) => {
+ const [formData, setFormData] = useState({
+ first_name: "",
+ last_name: "",
+ username: "",
+ email: "",
+ password: "",
+ confirmPassword: "",
+ });
+
+ const { register, isLoading, error } = useAuth();
+
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData({
+ ...formData,
+ [e.target.id]: e.target.value,
+ });
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ await register(formData);
+ };
+
+ return (
+
+ );
+};
diff --git a/src/modules/auth/hooks/useAuth.ts b/src/modules/auth/hooks/useAuth.ts
new file mode 100644
index 0000000..913f55a
--- /dev/null
+++ b/src/modules/auth/hooks/useAuth.ts
@@ -0,0 +1,81 @@
+import { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { authService } from "../api/auth.service";
+import { useApi } from "@/shared/api/hooks/use.api";
+import type {
+ LoginCredentials,
+ RegisterData,
+ OrganizationCreateData,
+} from "../types/auth.types";
+
+export const useAuth = () => {
+ const navigate = useNavigate();
+ const { isLoading, error, request } = useApi();
+ const [authError, setAuthError] = useState(null);
+
+ const login = async (credentials: LoginCredentials) => {
+ setAuthError(null);
+ const result = await request(() => authService.login(credentials));
+
+ if (result) {
+ authService.saveAuthData(result.access_token, result.user);
+
+ const orgs = await request(() => authService.getOrganizations());
+ if (orgs && orgs.length > 0) {
+ authService.saveOrganization(orgs[0]);
+ navigate("/");
+ } else {
+ navigate("/create-organization");
+ }
+ } else if (error) {
+ setAuthError(error);
+ }
+ };
+
+ const register = async (data: RegisterData) => {
+ setAuthError(null);
+
+ if (data.password !== data.confirmPassword) {
+ setAuthError("Пароли не совпадают");
+ return;
+ }
+
+ const { confirmPassword, ...registerData } = data;
+ const result = await request(() => authService.register(registerData));
+
+ if (result) {
+ authService.saveAuthData(result.access_token, result.user);
+ navigate("/create-organization");
+ } else if (error) {
+ setAuthError(error);
+ }
+ };
+
+ const createOrganization = async (data: OrganizationCreateData) => {
+ setAuthError(null);
+ const result = await request(() => authService.createOrganization(data));
+
+ if (result) {
+ authService.saveOrganization(result);
+ navigate("/");
+ } else if (error) {
+ setAuthError(error);
+ }
+ };
+
+ const logout = () => {
+ authService.logout();
+ navigate("/");
+ };
+
+ return {
+ login,
+ register,
+ createOrganization,
+ logout,
+ isLoading,
+ error: authError,
+ isAuthenticated: authService.isAuthenticated(),
+ hasOrganization: authService.hasOrganization(),
+ };
+};
diff --git a/src/modules/auth/types/auth.types.ts b/src/modules/auth/types/auth.types.ts
new file mode 100644
index 0000000..3bf3e6d
--- /dev/null
+++ b/src/modules/auth/types/auth.types.ts
@@ -0,0 +1,49 @@
+export interface LoginCredentials {
+ username: string;
+ password: string;
+}
+
+export interface RegisterData {
+ username: string;
+ email: string;
+ password: string;
+ confirmPassword: string;
+ first_name: string;
+ last_name: string;
+}
+
+export interface AuthResponse {
+ access_token: string;
+ token_type: string;
+ user: {
+ id: number;
+ username: string;
+ email: string;
+ first_name: string;
+ last_name: string;
+ };
+}
+
+export interface OrganizationCreateData {
+ name: string;
+ logo?: File;
+}
+
+export interface OrganizationResponse {
+ id: number;
+ name: string;
+ logo_url?: string;
+ created_at: string;
+ owner_id: number;
+}
+
+export interface OrganizationMember {
+ id: number;
+ user_id: number;
+ username: string;
+ email: string;
+ first_name: string;
+ last_name: string;
+ role: "owner" | "admin" | "member";
+ joined_at: string;
+}
diff --git a/src/modules/organization/components/OrganizationMembers.tsx b/src/modules/organization/components/OrganizationMembers.tsx
new file mode 100644
index 0000000..65d5983
--- /dev/null
+++ b/src/modules/organization/components/OrganizationMembers.tsx
@@ -0,0 +1,307 @@
+import React, { useState } from "react";
+import { useOrganization } from "../hooks/useOrganization";
+
+export const OrganizationMembers = () => {
+ const { members, isLoading, updateMemberRole, removeMember, addMember } =
+ useOrganization();
+ const [showAddModal, setShowAddModal] = useState(false);
+ const [newMemberEmail, setNewMemberEmail] = useState("");
+ const [newMemberRole, setNewMemberRole] = useState<"admin" | "member">(
+ "member",
+ );
+ const [actionLoading, setActionLoading] = useState(null);
+
+ const handleRoleChange = async (userId: number, newRole: string) => {
+ setActionLoading(userId);
+ await updateMemberRole(userId, newRole);
+ setActionLoading(null);
+ };
+
+ const handleRemoveMember = async (userId: number, memberName: string) => {
+ if (
+ confirm(
+ `Вы уверены, что хотите удалить пользователя "${memberName}" из организации?`,
+ )
+ ) {
+ setActionLoading(userId);
+ await removeMember(userId);
+ setActionLoading(null);
+ }
+ };
+
+ const handleAddMember = async (e: React.FormEvent) => {
+ e.preventDefault();
+ const success = await addMember(newMemberEmail, newMemberRole);
+ if (success) {
+ setShowAddModal(false);
+ setNewMemberEmail("");
+ setNewMemberRole("member");
+ }
+ };
+
+ const getRoleLabel = (role: string) => {
+ switch (role) {
+ case "owner":
+ return "Владелец";
+ case "admin":
+ return "Администратор";
+ case "member":
+ return "Участник";
+ default:
+ return role;
+ }
+ };
+
+ const getRoleColor = (role: string) => {
+ switch (role) {
+ case "owner":
+ return "text-yellow-600 dark:text-yellow-400";
+ case "admin":
+ return "text-blue-600 dark:text-blue-400";
+ case "member":
+ return "text-green-600 dark:text-green-400";
+ default:
+ return "";
+ }
+ };
+
+ if (isLoading && members.length === 0) {
+ return (
+
+
+ Загрузка участников...
+
+
+ );
+ }
+
+ return (
+
+
+
+ Участники организации
+
+
+
+
+
+
+
+
+ |
+ Имя
+ |
+
+ Email
+ |
+
+ Роль
+ |
+
+ Действия
+ |
+
+
+
+ {members.map((member) => (
+
+ |
+ {member.first_name} {member.last_name}
+
+ @{member.username}
+
+ |
+
+ {member.email}
+ |
+
+ {member.role === "owner" ? (
+
+ {getRoleLabel(member.role)}
+
+ ) : (
+
+ )}
+ |
+
+ {member.role !== "owner" && (
+
+ )}
+ |
+
+ ))}
+
+
+
+
+ {members.length === 0 && !isLoading && (
+
+ В организации пока нет участников
+
+ )}
+
+ {showAddModal && (
+
+
+
+ Добавить участника
+
+
+
+
+ )}
+
+ );
+};
diff --git a/src/modules/organization/hooks/useOrganization.ts b/src/modules/organization/hooks/useOrganization.ts
new file mode 100644
index 0000000..46478e3
--- /dev/null
+++ b/src/modules/organization/hooks/useOrganization.ts
@@ -0,0 +1,82 @@
+import { useState, useEffect } from "react";
+import { authService } from "@/modules/auth/api/auth.service";
+import { useApi } from "@/shared/api/hooks/use.api";
+import type { OrganizationMember } from "@/modules/auth/types/auth.types";
+
+export const useOrganization = () => {
+ const { isLoading, error, request } = useApi();
+ const [members, setMembers] = useState([]);
+ const [organization, setOrganization] = useState(
+ authService.getCurrentOrganization(),
+ );
+
+ const loadMembers = async () => {
+ if (organization) {
+ const result = await request(() =>
+ authService.getOrganizationMembers(organization.id),
+ );
+ if (result) {
+ setMembers(result);
+ }
+ }
+ };
+
+ useEffect(() => {
+ loadMembers();
+ }, [organization]);
+
+ const updateMemberRole = async (userId: number, newRole: string) => {
+ if (organization) {
+ const result = await request(() =>
+ authService.updateMemberRole(organization.id, userId, newRole),
+ );
+ if (result !== undefined) {
+ await loadMembers();
+ return true;
+ }
+ }
+ return false;
+ };
+
+ const removeMember = async (userId: number) => {
+ if (organization) {
+ const result = await request(() =>
+ authService.removeMember(organization.id, userId),
+ );
+ if (result !== undefined) {
+ await loadMembers();
+ return true;
+ }
+ }
+ return false;
+ };
+
+ const addMember = async (email: string, role: string) => {
+ if (organization) {
+ const result = await request(() =>
+ authService.addMember(organization.id, email, role),
+ );
+ if (result) {
+ await loadMembers();
+ return true;
+ }
+ }
+ return false;
+ };
+
+ const refreshOrganization = () => {
+ setOrganization(authService.getCurrentOrganization());
+ };
+
+ return {
+ members,
+ organization,
+ isLoading,
+ error,
+ updateMemberRole,
+ removeMember,
+ addMember,
+ refreshOrganization,
+ loadMembers,
+ };
+};
diff --git a/src/modules/theme-changer/config/theme.config.ts b/src/modules/theme-changer/config/theme.config.ts
new file mode 100644
index 0000000..0214bfc
--- /dev/null
+++ b/src/modules/theme-changer/config/theme.config.ts
@@ -0,0 +1,106 @@
+export const themes = [
+ {
+ id: "light",
+ name: "Светлая",
+ description: "Чистая светлая тема",
+ type: "light",
+ colors: {
+ primary: "#4f46e5",
+ background: "#ffffff",
+ surface: "#f8fafc",
+ text: "#1f2937",
+ border: "#e5e7eb",
+ },
+ },
+ {
+ id: "dark",
+ name: "Темная",
+ description: "Элегантная темная тема",
+ type: "dark",
+ colors: {
+ primary: "#6366f1",
+ background: "#0f172a",
+ surface: "#1e293b",
+ text: "#f1f5f9",
+ border: "#334155",
+ },
+ },
+ {
+ id: "nightowl",
+ name: "Night Owl",
+ description: "Тема вдохновленная редактором кода",
+ type: "dark",
+ colors: {
+ primary: "#7dd3fc",
+ background: "#011627",
+ surface: "#011e3c",
+ text: "#d6deeb",
+ border: "#1d3b53",
+ },
+ },
+ {
+ id: "sunset",
+ name: "Закат",
+ description: "Теплые оранжевые тона",
+ type: "dark",
+ colors: {
+ primary: "#f97316",
+ background: "#1c1917",
+ surface: "#292524",
+ text: "#fafaf9",
+ border: "#57534e",
+ },
+ },
+ {
+ id: "forest",
+ name: "Лес",
+ description: "Успокаивающая зеленая тема",
+ type: "dark",
+ colors: {
+ primary: "#22c55e",
+ background: "#052e16",
+ surface: "#14532d",
+ text: "#f0fdf4",
+ border: "#166534",
+ },
+ },
+ {
+ id: "ocean",
+ name: "Океан",
+ description: "Глубокие синие тона",
+ type: "dark",
+ colors: {
+ primary: "#06b6d4",
+ background: "#164e63",
+ surface: "#0e7490",
+ text: "#f0fdfd",
+ border: "#0891b2",
+ },
+ },
+ {
+ id: "lavender",
+ name: "Лаванда",
+ description: "Нежная фиолетовая тема",
+ type: "light",
+ colors: {
+ primary: "#a855f7",
+ background: "#faf5ff",
+ surface: "#f3e8ff",
+ text: "#581c87",
+ border: "#e9d5ff",
+ },
+ },
+ {
+ id: "coffee",
+ name: "Кофе",
+ description: "Уютная коричневая тема",
+ type: "dark",
+ colors: {
+ primary: "#d97706",
+ background: "#292524",
+ surface: "#44403c",
+ text: "#f5f5f4",
+ border: "#57534e",
+ },
+ },
+];
diff --git a/src/modules/theme-changer/index.ts b/src/modules/theme-changer/index.ts
new file mode 100644
index 0000000..b217a25
--- /dev/null
+++ b/src/modules/theme-changer/index.ts
@@ -0,0 +1,2 @@
+export { ThemeInitialProvider } from "./provider/theme.initial.provider";
+export { ThemeToggle } from "./ui/Theme.toggle";
diff --git a/src/modules/theme-changer/provider/theme.initial.provider.tsx b/src/modules/theme-changer/provider/theme.initial.provider.tsx
new file mode 100644
index 0000000..a70d022
--- /dev/null
+++ b/src/modules/theme-changer/provider/theme.initial.provider.tsx
@@ -0,0 +1,13 @@
+import { useLayoutEffect } from "react";
+import { applyTheme, initializeTheme } from "../utils/apply.theme";
+
+export const ThemeInitialProvider: React.FC<{ children: React.ReactNode }> = ({
+ children,
+}) => {
+ useLayoutEffect(() => {
+ const theme = initializeTheme();
+ applyTheme(theme);
+ }, []);
+
+ return children;
+};
diff --git a/src/modules/theme-changer/types/theme.type.ts b/src/modules/theme-changer/types/theme.type.ts
new file mode 100644
index 0000000..5bc74d2
--- /dev/null
+++ b/src/modules/theme-changer/types/theme.type.ts
@@ -0,0 +1,13 @@
+export interface ITheme {
+ id: string;
+ name: string;
+ description: string;
+ type: string;
+ colors: {
+ primary: string;
+ background: string;
+ surface: string;
+ text: string;
+ border: string;
+ };
+}
diff --git a/src/modules/theme-changer/ui/Theme.toggle.tsx b/src/modules/theme-changer/ui/Theme.toggle.tsx
new file mode 100644
index 0000000..c07dc22
--- /dev/null
+++ b/src/modules/theme-changer/ui/Theme.toggle.tsx
@@ -0,0 +1,68 @@
+import React, { useState, useEffect } from "react";
+import { FiSun, FiMoon } from "react-icons/fi";
+import {
+ getCurrentTheme,
+ toggleDarkLight,
+ getSavedTheme,
+} from "../../theme-changer/utils/apply.theme";
+import { themes } from "../../theme-changer/config/theme.config";
+
+export const ThemeToggle: React.FC = () => {
+ const [currentTheme, setCurrentTheme] = useState(() =>
+ getSavedTheme(),
+ );
+
+ const currentThemeData = themes.find((t) => t.id === currentTheme);
+ const isDark = currentThemeData?.type === "dark";
+
+ const handleClick = () => {
+ const newTheme = toggleDarkLight();
+ setCurrentTheme(newTheme);
+ };
+
+ // Инициализация при монтировании
+ useEffect(() => {
+ const saved = getSavedTheme();
+ const current = getCurrentTheme() || saved;
+ setCurrentTheme(current);
+ }, []);
+
+ // Слушаем изменения темы из других компонентов
+ useEffect(() => {
+ const handleThemeChange = (e: Event) => {
+ const event = e as CustomEvent;
+ setCurrentTheme(event.detail.theme);
+ };
+ window.addEventListener("themechange", handleThemeChange);
+ return () => window.removeEventListener("themechange", handleThemeChange);
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/src/modules/theme-changer/utils/apply.theme.ts b/src/modules/theme-changer/utils/apply.theme.ts
new file mode 100644
index 0000000..3d3fb26
--- /dev/null
+++ b/src/modules/theme-changer/utils/apply.theme.ts
@@ -0,0 +1,105 @@
+import { themes } from "../config/theme.config";
+
+export const applyTheme = (themeId: string) => {
+ const theme = themes.find((t) => t.id === themeId);
+ const root = document.documentElement;
+
+ if (theme) {
+ try {
+ root.setAttribute("data-theme", themeId);
+ localStorage.setItem("theme", themeId);
+ localStorage.setItem("theme-type", theme.type);
+
+ window.dispatchEvent(
+ new CustomEvent("themechange", {
+ detail: { theme: themeId, type: theme.type },
+ }),
+ );
+ } catch (error) {
+ console.error("❌ Error applying theme:", error);
+ }
+ } else {
+ console.warn(`⚠️ Theme not found: ${themeId}, falling back to light theme`);
+ applyTheme("light");
+ }
+};
+
+export const getSavedTheme = () => {
+ try {
+ return localStorage.getItem("theme") || "light";
+ } catch (error) {
+ console.error("Error reading theme from localStorage:", error);
+ return "light";
+ }
+};
+
+export const initializeTheme = () => {
+ const savedTheme = getSavedTheme();
+
+ const themeExists = themes.some((t) => t.id === savedTheme);
+ const themeToApply = themeExists ? savedTheme : "light";
+
+ applyTheme(themeToApply);
+ return themeToApply;
+};
+
+export const getCurrentTheme = () => {
+ return document.documentElement.getAttribute("data-theme") || "light";
+};
+
+export const getCurrentThemeType = () => {
+ const currentTheme = getCurrentTheme();
+ const theme = themes.find((t) => t.id === currentTheme);
+ return theme ? theme.type : "light";
+};
+
+export const toggleDarkLight = () => {
+ const currentTheme = getCurrentTheme();
+ const currentThemeData = themes.find((t) => t.id === currentTheme);
+
+ if (currentThemeData) {
+ const oppositeThemes = themes.filter(
+ (t) => t.type !== currentThemeData.type,
+ );
+ if (oppositeThemes.length > 0) {
+ applyTheme(oppositeThemes[0].id);
+ return oppositeThemes[0].id;
+ }
+ }
+
+ const newTheme = currentTheme === "light" ? "dark" : "light";
+ applyTheme(newTheme);
+ return newTheme;
+};
+
+export const getNextTheme = () => {
+ const currentTheme = getCurrentTheme();
+ const currentIndex = themes.findIndex((t) => t.id === currentTheme);
+ const nextIndex = (currentIndex + 1) % themes.length;
+ return themes[nextIndex].id;
+};
+
+export const applySystemTheme = () => {
+ if (
+ window.matchMedia &&
+ window.matchMedia("(prefers-color-scheme: dark)").matches
+ ) {
+ applyTheme("dark");
+ } else {
+ applyTheme("light");
+ }
+};
+
+export const watchSystemTheme = () => {
+ if (window.matchMedia) {
+ window
+ .matchMedia("(prefers-color-scheme: dark)")
+ .addEventListener("change", (e) => {
+ if (e.matches) {
+ applyTheme("dark");
+ } else {
+ applyTheme("light");
+ }
+ });
+ }
+};
diff --git a/src/pages/AuthPage.tsx b/src/pages/AuthPage.tsx
new file mode 100644
index 0000000..ca2cd28
--- /dev/null
+++ b/src/pages/AuthPage.tsx
@@ -0,0 +1,63 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { ThemeToggle } from "@/modules/theme-changer/ui/Theme.toggle";
+import { LoginForm } from "@/modules/auth/components/LoginForm";
+import { RegisterForm } from "@/modules/auth/components/RegisterForm";
+
+export const AuthPage = () => {
+ const [isLogin, setIsLogin] = useState(true);
+ const navigate = useNavigate();
+
+ return (
+
+
+
+
+
+
+
+
+
+ IPS Manager
+
+
+ {isLogin
+ ? "Войдите в систему для управления IPS агентами"
+ : "Создайте аккаунт для начала работы"}
+
+
+
+ {isLogin ? (
+
setIsLogin(false)} />
+ ) : (
+ setIsLogin(true)} />
+ )}
+
+
+ );
+};
diff --git a/src/pages/CreateOrganizationPage.tsx b/src/pages/CreateOrganizationPage.tsx
new file mode 100644
index 0000000..e94af62
--- /dev/null
+++ b/src/pages/CreateOrganizationPage.tsx
@@ -0,0 +1,52 @@
+import { ThemeToggle } from "@/modules/theme-changer/ui/Theme.toggle";
+import { CreateOrganizationForm } from "@/modules/auth/components/CreateOrganizationForm";
+import { useAuth } from "@/modules/auth/hooks/useAuth";
+import { Navigate } from "react-router-dom";
+
+export const CreateOrganizationPage = () => {
+ const { isAuthenticated, hasOrganization } = useAuth();
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ if (hasOrganization) {
+ return ;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ Создание организации
+
+
+ Создайте организацию для начала работы с IPS платформой
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/OrganizationPage.tsx b/src/pages/OrganizationPage.tsx
new file mode 100644
index 0000000..35fee42
--- /dev/null
+++ b/src/pages/OrganizationPage.tsx
@@ -0,0 +1,74 @@
+import React from "react";
+import { ThemeToggle } from "@/modules/theme-changer/ui/Theme.toggle";
+import { OrganizationMembers } from "@/modules/organization/components/OrganizationMembers";
+import { useAuth } from "@/modules/auth/hooks/useAuth";
+import { Navigate, useNavigate } from "react-router-dom";
+import { authService } from "@/modules/auth/api/auth.service";
+
+export const OrganizationPage = () => {
+ const { isAuthenticated, hasOrganization } = useAuth();
+ const navigate = useNavigate();
+ const organization = authService.getCurrentOrganization();
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ if (!hasOrganization) {
+ return ;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {organization?.logo_url && (
+

+ )}
+
+
+ {organization?.name}
+
+
+ Управление участниками организации
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/home.page.tsx b/src/pages/home.page.tsx
new file mode 100644
index 0000000..334f209
--- /dev/null
+++ b/src/pages/home.page.tsx
@@ -0,0 +1,723 @@
+import { ThemeToggle } from "@/modules/theme-changer/ui/Theme.toggle";
+import { useNavigate } from "react-router-dom";
+import { useAuth } from "@/modules/auth/hooks/useAuth";
+import { authService } from "@/modules/auth/api/auth.service";
+import {
+ Shield,
+ Users,
+ Bot,
+ Activity,
+ LogOut,
+ ChevronRight,
+ Zap,
+ Lock,
+ Cloud,
+ TrendingUp,
+ Bell,
+ Server,
+ Sparkles,
+ AlertCircle,
+ CheckCircle,
+ Clock,
+ LogIn,
+ UserPlus,
+} from "lucide-react";
+
+export const HomePage = () => {
+ const navigate = useNavigate();
+ const { logout } = useAuth();
+ const isAuthenticated = authService.isAuthenticated();
+ const organization = authService.getCurrentOrganization();
+ const authStorage = localStorage.getItem("auth-storage");
+ const user = authStorage ? JSON.parse(authStorage).state?.user : null;
+
+ const stats = [
+ { label: "Активные агенты", value: "12", change: "+2", icon: Server },
+ { label: "Заблокировано IP", value: "1,284", change: "+128", icon: Shield },
+ { label: "Активных правил", value: "47", change: "+5", icon: Lock },
+ {
+ label: "Атак предотвращено",
+ value: "3,721",
+ change: "+342",
+ icon: TrendingUp,
+ },
+ ];
+
+ const recentActivities = [
+ {
+ id: 1,
+ type: "attack",
+ message: "Обнаружена brute-force атака с IP 192.168.1.45",
+ time: "2 минуты назад",
+ severity: "high",
+ },
+ {
+ id: 2,
+ type: "rule",
+ message: "Новое правило фильтрации добавлено администратором",
+ time: "15 минут назад",
+ severity: "info",
+ },
+ {
+ id: 3,
+ type: "agent",
+ message: "Агент на сервере web-01 успешно обновлен",
+ time: "1 час назад",
+ severity: "success",
+ },
+ {
+ id: 4,
+ type: "ai",
+ message: "AI предложил новые правила для обнаружения ботов",
+ time: "3 часа назад",
+ severity: "info",
+ },
+ ];
+
+ const features = [
+ {
+ title: "Централизованное управление",
+ description: "Управляйте всеми IPS агентами из единого интерфейса",
+ icon: Server,
+ color: "#6366f1",
+ },
+ {
+ title: "AI Аналитика",
+ description: "Искусственный интеллект помогает находить сложные атаки",
+ icon: Bot,
+ color: "#8b5cf6",
+ },
+ {
+ title: "Мгновенная блокировка",
+ description: "Автоматическая блокировка IP при обнаружении угроз",
+ icon: Zap,
+ color: "#f59e0b",
+ },
+ {
+ title: "Безопасность данных",
+ description: "LLM не получает доступ к чувствительным логам",
+ icon: Lock,
+ color: "#22c55e",
+ },
+ ];
+
+ const quickActions = isAuthenticated
+ ? [
+ {
+ name: "IPS Агенты",
+ icon: Server,
+ path: "/agents",
+ description: "Управление агентами",
+ color: "#6366f1",
+ },
+ {
+ name: "Правила",
+ icon: Shield,
+ path: "/rules",
+ description: "Правила фильтрации",
+ color: "#22c55e",
+ },
+ {
+ name: "AI Аналитика",
+ icon: Bot,
+ path: "/ai-analytics",
+ description: "Анализ логов",
+ color: "#8b5cf6",
+ },
+ {
+ name: "Команда",
+ icon: Users,
+ path: "/organization",
+ description: "Управление доступом",
+ color: "#f59e0b",
+ },
+ ]
+ : [];
+
+ const getSeverityStyles = (severity: string) => {
+ switch (severity) {
+ case "high":
+ return {
+ bg: "#ef444410",
+ border: "#ef4444",
+ icon: AlertCircle,
+ text: "#ef4444",
+ };
+ case "success":
+ return {
+ bg: "#22c55e10",
+ border: "#22c55e",
+ icon: CheckCircle,
+ text: "#22c55e",
+ };
+ default:
+ return {
+ bg: "#6366f110",
+ border: "#6366f1",
+ icon: Sparkles,
+ text: "#6366f1",
+ };
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+
+ {/* Logo */}
+
+
+
+
+
+
+ IPS Manager
+
+ {organization && (
+
+ {organization.name}
+
+ )}
+
+
+
+ {/* Right section */}
+
+ {isAuthenticated ? (
+ <>
+
+
+
+
+ {user?.first_name?.[0]}
+ {user?.last_name?.[0]}
+
+
+
+ {user?.first_name} {user?.last_name}
+
+
+ Администратор
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+
+ >
+ )}
+
+
+
+
+
+
+ {/* Hero Section */}
+
+
+
+
+
+ {isAuthenticated
+ ? `Добро пожаловать, ${user?.first_name || "Администратор"}!`
+ : "Добро пожаловать в IPS Manager"}
+
+
+ {isAuthenticated
+ ? "Система IPS мониторинга и защиты"
+ : "Современная платформа для централизованного управления IPS агентами"}
+
+ {!isAuthenticated && (
+
+
+
+
+ )}
+ {isAuthenticated && (
+
+
+
+ Все системы работают стабильно
+
+
+ )}
+
+
+
+
+
+
+
+
+ {/* Stats Grid - только для авторизованных */}
+ {isAuthenticated && (
+
+ {stats.map((stat, index) => {
+ const Icon = stat.icon;
+ return (
+
+
+
+
+
+
+ {stat.change}
+
+
+
+ {stat.value}
+
+
+ {stat.label}
+
+
+ );
+ })}
+
+ )}
+
+ {/* Features Section - для всех */}
+
+
+
+ Ключевые возможности
+
+
+ Всё необходимое для защиты вашей инфраструктуры
+
+
+
+ {features.map((feature, index) => {
+ const Icon = feature.icon;
+ return (
+
+
+
+
+
+ {feature.title}
+
+
+ {feature.description}
+
+
+ );
+ })}
+
+
+
+ {isAuthenticated ? (
+ <>
+
+ {/* Quick Actions */}
+
+
+
+ Быстрый доступ
+
+
+ {quickActions.map((action, index) => {
+ const Icon = action.icon;
+ return (
+
+ );
+ })}
+
+
+
+
+ {/* Recent Activity */}
+
+
+
+ Последние события
+
+
+
+
+ {recentActivities.map((activity) => {
+ const severityStyles = getSeverityStyles(activity.severity);
+ const Icon = severityStyles.icon;
+ return (
+
+
+
+
+
+
+
+ {activity.message}
+
+
+
+
+ {activity.time}
+
+
+
+
+
+ );
+ })}
+
+
+
+
+
+ {/* AI Insights */}
+
+
+
+
+
+
+
+
+ AI Аналитика
+
+
+ Новые предложения на основе анализа логов
+
+
+
+
+
+
+
+
+ 🔍 Обнаружена аномалия
+
+
+ Повышенная активность с IP-адресов из диапазона
+ 185.xxx.xx.xx
+
+
+
+
+ 💡 Предложено правил
+
+
+ AI предлагает 3 новых правила для блокировки бот-сканеров
+
+
+
+
+ ⚡ Оптимизация
+
+
+ Рекомендуется обновить 5 существующих правил для повышения
+ эффективности
+
+
+
+
+ >
+ ) : (
+ /* CTA Section для неавторизованных */
+
+
+ Готовы начать?
+
+
+ Присоединяйтесь к IPS Manager и получите полный контроль над
+ безопасностью
+
+
+
+ )}
+
+
+ );
+};
diff --git a/src/pages/secondary.page.tsx b/src/pages/secondary.page.tsx
new file mode 100644
index 0000000..72b729d
--- /dev/null
+++ b/src/pages/secondary.page.tsx
@@ -0,0 +1,7 @@
+export const SecondaryPage = () => {
+ return(
+
+ Вторичная
+
+ )
+}
\ No newline at end of file
diff --git a/src/shared/api/api.service.ts b/src/shared/api/api.service.ts
new file mode 100644
index 0000000..112a5bf
--- /dev/null
+++ b/src/shared/api/api.service.ts
@@ -0,0 +1,37 @@
+import { apiClient } from "./axios.instance";
+import type { AxiosResponse } from "axios";
+
+export interface ApiResponse {
+ data: T;
+ message: string;
+ success: boolean;
+}
+
+class ApiService {
+ async get(url: string, config?: any): Promise {
+ const response: AxiosResponse = await apiClient.get(url, config);
+ return response.data;
+ }
+
+ async post(url: string, data?: D, config?: any): Promise {
+ const response: AxiosResponse = await apiClient.post(url, data, config);
+ return response.data;
+ }
+
+ async put(url: string, data?: D, config?: any): Promise {
+ const response: AxiosResponse = await apiClient.put(url, data, config);
+ return response.data;
+ }
+
+ async patch(url: string, data?: D, config?: any): Promise {
+ const response: AxiosResponse = await apiClient.patch(url, data, config);
+ return response.data;
+ }
+
+ async delete(url: string, config?: any): Promise {
+ const response: AxiosResponse = await apiClient.delete(url, config);
+ return response.data;
+ }
+}
+
+export const apiService = new ApiService();
diff --git a/src/shared/api/axios.instance.ts b/src/shared/api/axios.instance.ts
new file mode 100644
index 0000000..e5381c3
--- /dev/null
+++ b/src/shared/api/axios.instance.ts
@@ -0,0 +1,104 @@
+import axios, {
+ type AxiosInstance,
+ type AxiosResponse,
+ type AxiosError,
+ type InternalAxiosRequestConfig,
+} from "axios";
+
+export interface ApiResponse {
+ data: T;
+ message?: string;
+ status: number;
+}
+
+class ApiClient {
+ private axiosInstance: AxiosInstance;
+
+ constructor() {
+ this.axiosInstance = axios.create({
+ baseURL: "http://213.165.213.170:8080/api/v1",
+ timeout: 10000,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ validateStatus: (status) => {
+ return status >= 200 && status < 400;
+ },
+ // Добавляем кастомный сериализатор параметров
+ paramsSerializer: {
+ serialize: (params) => {
+ const parts: string[] = [];
+
+ Object.entries(params).forEach(([key, value]) => {
+ if (value === undefined || value === null) return;
+
+ if (Array.isArray(value)) {
+ // Преобразуем массив в множественные параметры: level=info&level=warning
+ value.forEach((item) => {
+ if (item !== undefined && item !== null) {
+ parts.push(
+ `${encodeURIComponent(key)}=${encodeURIComponent(item)}`,
+ );
+ }
+ });
+ } else {
+ parts.push(
+ `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,
+ );
+ }
+ });
+
+ return parts.join("&");
+ },
+ },
+ });
+
+ this.setupInterceptors();
+ }
+
+ private setupInterceptors(): void {
+ this.axiosInstance.interceptors.request.use(
+ (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
+ // Получаем токен из localStorage
+ const authStorage = localStorage.getItem("auth-storage");
+ if (authStorage) {
+ try {
+ const parsed = JSON.parse(authStorage);
+ const token = parsed.state?.token;
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ } catch (e) {
+ console.error("[Auth] Failed to parse auth storage:", e);
+ }
+ }
+ return config;
+ },
+ (error: AxiosError): Promise => {
+ console.error("[Request Error]", error);
+ return Promise.reject(error);
+ },
+ );
+
+ this.axiosInstance.interceptors.response.use(
+ (response: AxiosResponse): AxiosResponse => {
+ console.log(`[Response] ${response.status} ${response.config.url}`);
+ return response;
+ },
+ async (error: AxiosError): Promise => {
+ if (error.response?.status === 401) {
+ window.location.href = "/auth";
+ return Promise.reject(error);
+ }
+
+ return Promise.reject(error);
+ },
+ );
+ }
+
+ public getInstance(): AxiosInstance {
+ return this.axiosInstance;
+ }
+}
+
+export const apiClient = new ApiClient().getInstance();
diff --git a/src/shared/api/hooks/use.api.ts b/src/shared/api/hooks/use.api.ts
new file mode 100644
index 0000000..1f15b55
--- /dev/null
+++ b/src/shared/api/hooks/use.api.ts
@@ -0,0 +1,32 @@
+import { useState, useCallback } from "react";
+
+export function useApi() {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const request = useCallback(
+ async (apiCall: () => Promise): Promise => {
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ const result = await apiCall();
+ return result;
+ } catch (err: any) {
+ const errorMessage =
+ err.response?.data?.message || err.message || "Произошла ошибка";
+ setError(errorMessage);
+ return undefined;
+ } finally {
+ setIsLoading(false);
+ }
+ },
+ [],
+ );
+
+ return {
+ isLoading,
+ error,
+ request,
+ };
+}
diff --git a/src/shared/api/index.ts b/src/shared/api/index.ts
new file mode 100644
index 0000000..c98f5ac
--- /dev/null
+++ b/src/shared/api/index.ts
@@ -0,0 +1,2 @@
+export { apiClient } from "./axios.instance";
+export { useApi } from "./hooks/use.api";
diff --git a/src/shared/styles/index.css b/src/shared/styles/index.css
new file mode 100644
index 0000000..580b131
--- /dev/null
+++ b/src/shared/styles/index.css
@@ -0,0 +1,14 @@
+@import "tailwindcss";
+@import "./normalize.css";
+@import "./root.css";
+@import "./themes.css";
+
+/* Hide scrollbar but keep functionality */
+.scrollbar-hide {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+}
+
+.scrollbar-hide::-webkit-scrollbar {
+ display: none; /* Chrome, Safari and Opera */
+}
diff --git a/src/shared/styles/normalize.css b/src/shared/styles/normalize.css
new file mode 100644
index 0000000..e7c7aac
--- /dev/null
+++ b/src/shared/styles/normalize.css
@@ -0,0 +1,365 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+ ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+ line-height: 1.15;
+ /* 1 */
+ -webkit-text-size-adjust: 100%;
+ /* 2 */
+}
+
+/* Sections
+ ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+ overflow-x: hidden;
+ margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+
+main {
+ display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+ font-size: 2em;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+ box-sizing: content-box;
+ /* 1 */
+ height: 0;
+ /* 1 */
+ overflow: visible;
+ /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+ font-family: monospace, monospace;
+ /* 1 */
+ font-size: 1em;
+ /* 2 */
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+/* a {
+ background-color: transparent;
+} */
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+ border-bottom: none;
+ /* 1 */
+ text-decoration: underline;
+ /* 2 */
+ text-decoration: underline dotted;
+ /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+ font-family: monospace, monospace;
+ /* 1 */
+ font-size: 1em;
+ /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+ border-style: none;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input {
+ /* 1 */
+ overflow: visible;
+ outline: none;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select {
+ /* 1 */
+ text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+ padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ * `fieldset` elements in all browsers.
+ */
+
+legend {
+ box-sizing: border-box;
+ /* 1 */
+ color: inherit;
+ /* 2 */
+ display: table;
+ /* 1 */
+ max-width: 100%;
+ /* 1 */
+ padding: 0;
+ /* 3 */
+ white-space: normal;
+ /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+ vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+ box-sizing: border-box;
+ /* 1 */
+ padding: 0;
+ /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+ -webkit-appearance: textfield;
+ /* 1 */
+ outline-offset: -2px;
+ /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ /* 1 */
+ font: inherit;
+ /* 2 */
+}
+
+/* Interactive
+ ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+ display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+ display: list-item;
+}
+
+/* Misc
+ ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+ display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+ display: none;
+}
diff --git a/src/shared/styles/root.css b/src/shared/styles/root.css
new file mode 100644
index 0000000..e0312e1
--- /dev/null
+++ b/src/shared/styles/root.css
@@ -0,0 +1,307 @@
+/* Дополнительные стили для PrimeReact с вашей темой */
+.p-tabmenu .p-tabmenuitem .p-menuitem-link {
+ color: var(--text-secondary);
+ transition: all 0.2s ease;
+}
+
+.p-tabmenu .p-tabmenuitem .p-menuitem-link:not(.p-disabled):hover {
+ color: var(--text-primary);
+ background-color: var(--bg-tertiary);
+}
+
+.p-tabmenu .p-tabmenuitem.p-highlight .p-menuitem-link {
+ color: var(--accent-primary);
+ border-color: var(--accent-primary);
+}
+
+.p-menubar {
+ background-color: var(--bg-secondary);
+ border: none;
+ border-radius: 0;
+}
+
+.p-menubar .p-menuitem-link {
+ color: var(--text-secondary);
+}
+
+.p-menubar .p-menuitem-link:hover {
+ background-color: var(--bg-tertiary);
+ color: var(--text-primary);
+}
+
+.p-button.p-button-text {
+ color: var(--text-secondary);
+}
+
+.p-button.p-button-text:hover {
+ color: var(--text-primary);
+ background-color: var(--bg-tertiary);
+}
+
+/* ==================== Стили для скроллов ==================== */
+
+/* WebKit браузеры (Chrome, Safari, Edge, Opera) */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 4px;
+ transition: background 0.2s ease;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+::-webkit-scrollbar-corner {
+ background: var(--bg-tertiary);
+}
+
+/* Firefox */
+* {
+ scrollbar-width: thin;
+ scrollbar-color: var(--border-secondary) var(--bg-tertiary);
+}
+
+/* Для элементов с прокруткой (кастомные классы) */
+.custom-scrollbar {
+ scrollbar-width: thin;
+ scrollbar-color: var(--border-secondary) var(--bg-tertiary);
+}
+
+.custom-scrollbar::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 4px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 4px;
+ transition: background 0.2s ease;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Для горизонтальных скроллов */
+.horizontal-scrollbar {
+ overflow-x: auto;
+}
+
+.horizontal-scrollbar::-webkit-scrollbar {
+ height: 6px;
+}
+
+/* Для очень тонких скроллов (например, в таблицах) */
+.thin-scrollbar::-webkit-scrollbar {
+ width: 4px;
+ height: 4px;
+}
+
+.thin-scrollbar::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 2px;
+}
+
+.thin-scrollbar::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 2px;
+}
+
+.thin-scrollbar::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Для темных тем - дополнительная стилизация */
+[data-theme="dark"] ::-webkit-scrollbar-track,
+[data-theme="nightowl"] ::-webkit-scrollbar-track,
+[data-theme="sunset"] ::-webkit-scrollbar-track,
+[data-theme="forest"] ::-webkit-scrollbar-track,
+[data-theme="ocean"] ::-webkit-scrollbar-track,
+[data-theme="coffee"] ::-webkit-scrollbar-track,
+[data-theme="midnight"] ::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+[data-theme="dark"] ::-webkit-scrollbar-thumb,
+[data-theme="nightowl"] ::-webkit-scrollbar-thumb,
+[data-theme="sunset"] ::-webkit-scrollbar-thumb,
+[data-theme="forest"] ::-webkit-scrollbar-thumb,
+[data-theme="ocean"] ::-webkit-scrollbar-thumb,
+[data-theme="coffee"] ::-webkit-scrollbar-thumb,
+[data-theme="midnight"] ::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+[data-theme="dark"] ::-webkit-scrollbar-thumb:hover,
+[data-theme="nightowl"] ::-webkit-scrollbar-thumb:hover,
+[data-theme="sunset"] ::-webkit-scrollbar-thumb:hover,
+[data-theme="forest"] ::-webkit-scrollbar-thumb:hover,
+[data-theme="ocean"] ::-webkit-scrollbar-thumb:hover,
+[data-theme="coffee"] ::-webkit-scrollbar-thumb:hover,
+[data-theme="midnight"] ::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Для светлых тем - более контрастные скроллы */
+[data-theme="light"] ::-webkit-scrollbar-track {
+ background: #f1f5f9;
+}
+
+[data-theme="light"] ::-webkit-scrollbar-thumb {
+ background: #cbd5e1;
+}
+
+[data-theme="light"] ::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Для лавандовой темы */
+[data-theme="lavender"] ::-webkit-scrollbar-track {
+ background: #e9d5ff;
+}
+
+[data-theme="lavender"] ::-webkit-scrollbar-thumb {
+ background: #c084fc;
+}
+
+[data-theme="lavender"] ::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Для розовой темы */
+[data-theme="rose"] ::-webkit-scrollbar-track {
+ background: #fecdd3;
+}
+
+[data-theme="rose"] ::-webkit-scrollbar-thumb {
+ background: #fb7185;
+}
+
+[data-theme="rose"] ::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Стили для скролла в текстовых полях и textarea */
+textarea::-webkit-scrollbar {
+ width: 6px;
+}
+
+textarea::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 3px;
+}
+
+textarea::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 3px;
+}
+
+textarea::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Стили для скролла в выпадающих списках PrimeReact */
+.p-dropdown-panel .p-dropdown-items-wrapper::-webkit-scrollbar {
+ width: 6px;
+}
+
+.p-dropdown-panel .p-dropdown-items-wrapper::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 3px;
+}
+
+.p-dropdown-panel .p-dropdown-items-wrapper::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 3px;
+}
+
+.p-dropdown-panel .p-dropdown-items-wrapper::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Стили для скролла в таблицах */
+.p-datatable-wrapper::-webkit-scrollbar {
+ height: 8px;
+ width: 8px;
+}
+
+.p-datatable-wrapper::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 4px;
+}
+
+.p-datatable-wrapper::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 4px;
+}
+
+.p-datatable-wrapper::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Стили для скролла в модальных окнах */
+.p-dialog .p-dialog-content::-webkit-scrollbar {
+ width: 6px;
+}
+
+.p-dialog .p-dialog-content::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 3px;
+}
+
+.p-dialog .p-dialog-content::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 3px;
+}
+
+.p-dialog .p-dialog-content::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+/* Стили для скролла в меню */
+.p-menu .p-menu-list::-webkit-scrollbar {
+ width: 6px;
+}
+
+.p-menu .p-menu-list::-webkit-scrollbar-track {
+ background: var(--bg-tertiary);
+ border-radius: 3px;
+}
+
+.p-menu .p-menu-list::-webkit-scrollbar-thumb {
+ background: var(--border-secondary);
+ border-radius: 3px;
+}
+
+.p-menu .p-menu-list::-webkit-scrollbar-thumb:hover {
+ background: var(--accent-primary);
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+.animate-pulse {
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}
diff --git a/src/shared/styles/themes.css b/src/shared/styles/themes.css
new file mode 100644
index 0000000..465ad9a
--- /dev/null
+++ b/src/shared/styles/themes.css
@@ -0,0 +1,267 @@
+@import "tailwindcss";
+
+/* Tailwind dark mode через data-theme атрибут */
+@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
+
+/* Кастомные утилиты */
+@layer utilities {
+ .transition-theme {
+ transition-property: background-color, border-color, color, fill, stroke;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 200ms;
+ }
+}
+
+/* ===========================
+ БАЗОВЫЕ ТЕМЫ (Light/Dark)
+ =========================== */
+
+/* Светлая тема (по умолчанию) */
+:root,
+[data-theme="light"] {
+ --bg-primary: #ffffff;
+ --bg-secondary: #f8fafc;
+ --bg-tertiary: #f1f5f9;
+ --text-primary: #0f172a;
+ --text-secondary: #475569;
+ --text-muted: #94a3b8;
+ --border: #e2e8f0;
+ --border-focus: #94a3b8;
+ --card-bg: #ffffff;
+ --input-bg: #ffffff;
+ --header-bg: #ffffff;
+ --button-primary: #0f172a;
+ --button-primary-text: #ffffff;
+ --button-primary-hover: #1e293b;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #dc2626;
+ --error-bg: #fef2f2;
+ --error-border: #fecaca;
+ --error-text: #dc2626;
+ --shadow-color: rgba(0, 0, 0, 0.1);
+ --accent: #6366f1;
+ --accent-hover: #4f46e5;
+ --link: #0f172a;
+ --link-hover: #475569;
+}
+
+/* Темная тема */
+[data-theme="dark"] {
+ --bg-primary: #0f172a;
+ --bg-secondary: #1e293b;
+ --bg-tertiary: #334155;
+ --text-primary: #f8fafc;
+ --text-secondary: #cbd5e1;
+ --text-muted: #64748b;
+ --border: #334155;
+ --border-focus: #64748b;
+ --card-bg: #1e293b;
+ --input-bg: #1e293b;
+ --header-bg: #0f172a;
+ --button-primary: #f8fafc;
+ --button-primary-text: #0f172a;
+ --button-primary-hover: #e2e8f0;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #f87171;
+ --error-bg: rgba(239, 68, 68, 0.1);
+ --error-border: rgba(239, 68, 68, 0.3);
+ --error-text: #fca5a5;
+ --shadow-color: rgba(0, 0, 0, 0.3);
+ --accent: #818cf8;
+ --accent-hover: #a5b4fc;
+ --link: #f8fafc;
+ --link-hover: #cbd5e1;
+}
+
+/* ===========================
+ ЦВЕТНЫЕ ТЕМЫ
+ =========================== */
+
+/* Night Owl */
+[data-theme="nightowl"] {
+ --bg-primary: #011627;
+ --bg-secondary: #011e3c;
+ --bg-tertiary: #0d2f4f;
+ --text-primary: #d6deeb;
+ --text-secondary: #8892b0;
+ --text-muted: #4a5568;
+ --border: #1d3b53;
+ --border-focus: #7dd3fc;
+ --card-bg: #011e3c;
+ --input-bg: #011e3c;
+ --header-bg: #011627;
+ --button-primary: #7dd3fc;
+ --button-primary-text: #011627;
+ --button-primary-hover: #bae6fd;
+ --button-danger: #f87171;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #fca5a5;
+ --error-bg: rgba(248, 113, 113, 0.1);
+ --error-border: rgba(248, 113, 113, 0.3);
+ --error-text: #fca5a5;
+ --shadow-color: rgba(0, 0, 0, 0.4);
+ --accent: #7dd3fc;
+ --accent-hover: #bae6fd;
+ --link: #7dd3fc;
+ --link-hover: #bae6fd;
+}
+
+/* Sunset */
+[data-theme="sunset"] {
+ --bg-primary: #1c1917;
+ --bg-secondary: #292524;
+ --bg-tertiary: #44403c;
+ --text-primary: #fafaf9;
+ --text-secondary: #d6d3d1;
+ --text-muted: #78716c;
+ --border: #57534e;
+ --border-focus: #f97316;
+ --card-bg: #292524;
+ --input-bg: #292524;
+ --header-bg: #1c1917;
+ --button-primary: #f97316;
+ --button-primary-text: #1c1917;
+ --button-primary-hover: #fb923c;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #f87171;
+ --error-bg: rgba(239, 68, 68, 0.1);
+ --error-border: rgba(239, 68, 68, 0.3);
+ --error-text: #fca5a5;
+ --shadow-color: rgba(0, 0, 0, 0.4);
+ --accent: #f97316;
+ --accent-hover: #fb923c;
+ --link: #f97316;
+ --link-hover: #fb923c;
+}
+
+/* Forest */
+[data-theme="forest"] {
+ --bg-primary: #052e16;
+ --bg-secondary: #14532d;
+ --bg-tertiary: #166534;
+ --text-primary: #f0fdf4;
+ --text-secondary: #bbf7d0;
+ --text-muted: #4ade80;
+ --border: #166534;
+ --border-focus: #22c55e;
+ --card-bg: #14532d;
+ --input-bg: #14532d;
+ --header-bg: #052e16;
+ --button-primary: #22c55e;
+ --button-primary-text: #052e16;
+ --button-primary-hover: #4ade80;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #f87171;
+ --error-bg: rgba(239, 68, 68, 0.1);
+ --error-border: rgba(239, 68, 68, 0.3);
+ --error-text: #fca5a5;
+ --shadow-color: rgba(0, 0, 0, 0.4);
+ --accent: #22c55e;
+ --accent-hover: #4ade80;
+ --link: #22c55e;
+ --link-hover: #4ade80;
+}
+
+/* Ocean */
+[data-theme="ocean"] {
+ --bg-primary: #164e63;
+ --bg-secondary: #0e7490;
+ --bg-tertiary: #0891b2;
+ --text-primary: #f0fdfd;
+ --text-secondary: #a5f3fc;
+ --text-muted: #22d3ee;
+ --border: #0891b2;
+ --border-focus: #06b6d4;
+ --card-bg: #0e7490;
+ --input-bg: #0e7490;
+ --header-bg: #164e63;
+ --button-primary: #06b6d4;
+ --button-primary-text: #164e63;
+ --button-primary-hover: #22d3ee;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #f87171;
+ --error-bg: rgba(239, 68, 68, 0.1);
+ --error-border: rgba(239, 68, 68, 0.3);
+ --error-text: #fca5a5;
+ --shadow-color: rgba(0, 0, 0, 0.4);
+ --accent: #06b6d4;
+ --accent-hover: #22d3ee;
+ --link: #06b6d4;
+ --link-hover: #22d3ee;
+}
+
+/* Lavender */
+[data-theme="lavender"] {
+ --bg-primary: #faf5ff;
+ --bg-secondary: #f3e8ff;
+ --bg-tertiary: #e9d5ff;
+ --text-primary: #581c87;
+ --text-secondary: #7e22ce;
+ --text-muted: #a855f7;
+ --border: #e9d5ff;
+ --border-focus: #a855f7;
+ --card-bg: #f3e8ff;
+ --input-bg: #f3e8ff;
+ --header-bg: #faf5ff;
+ --button-primary: #a855f7;
+ --button-primary-text: #faf5ff;
+ --button-primary-hover: #c084fc;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #f87171;
+ --error-bg: rgba(239, 68, 68, 0.1);
+ --error-border: rgba(239, 68, 68, 0.3);
+ --error-text: #dc2626;
+ --shadow-color: rgba(88, 28, 135, 0.1);
+ --accent: #a855f7;
+ --accent-hover: #c084fc;
+ --link: #7e22ce;
+ --link-hover: #a855f7;
+}
+
+/* Coffee */
+[data-theme="coffee"] {
+ --bg-primary: #292524;
+ --bg-secondary: #44403c;
+ --bg-tertiary: #57534e;
+ --text-primary: #f5f5f4;
+ --text-secondary: #d6d3d1;
+ --text-muted: #a8a29e;
+ --border: #57534e;
+ --border-focus: #d97706;
+ --card-bg: #44403c;
+ --input-bg: #44403c;
+ --header-bg: #292524;
+ --button-primary: #d97706;
+ --button-primary-text: #292524;
+ --button-primary-hover: #fbbf24;
+ --button-danger: #ef4444;
+ --button-danger-text: #ffffff;
+ --button-danger-hover: #f87171;
+ --error-bg: rgba(239, 68, 68, 0.1);
+ --error-border: rgba(239, 68, 68, 0.3);
+ --error-text: #fca5a5;
+ --shadow-color: rgba(0, 0, 0, 0.4);
+ --accent: #d97706;
+ --accent-hover: #fbbf24;
+ --link: #d97706;
+ --link-hover: #fbbf24;
+}
+
+/* ===========================
+ БАЗОВЫЕ СТИЛИ
+ =========================== */
+
+body {
+ background-color: var(--bg-primary);
+ color: var(--text-primary);
+ transition:
+ background-color 0.3s ease,
+ color 0.3s ease,
+ border-color 0.3s ease;
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 7f42e5f..a90b99c 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -7,6 +7,11 @@
"types": ["vite/client"],
"skipLibCheck": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ },
+
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
diff --git a/vite.config.ts b/vite.config.ts
index 8b0f57b..5d1e6ad 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,7 +1,14 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from "vite";
+import path from "path";
+import react from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/
export default defineConfig({
- plugins: [react()],
-})
+ plugins: [react(), tailwindcss()],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "src"),
+ },
+ },
+});
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..3106b18
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,1619 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7"
+ integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.29.7"
+ js-tokens "^4.0.0"
+ picocolors "^1.1.1"
+
+"@babel/compat-data@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.7.tgz#6f0237f0f36d2e51c0570a636faed9d2d0efe629"
+ integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==
+
+"@babel/core@^7.24.4":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.7.tgz#80c10b17248082968b57a857b91640971f2070f7"
+ integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==
+ dependencies:
+ "@babel/code-frame" "^7.29.7"
+ "@babel/generator" "^7.29.7"
+ "@babel/helper-compilation-targets" "^7.29.7"
+ "@babel/helper-module-transforms" "^7.29.7"
+ "@babel/helpers" "^7.29.7"
+ "@babel/parser" "^7.29.7"
+ "@babel/template" "^7.29.7"
+ "@babel/traverse" "^7.29.7"
+ "@babel/types" "^7.29.7"
+ "@jridgewell/remapping" "^2.3.5"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
+"@babel/generator@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3"
+ integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==
+ dependencies:
+ "@babel/parser" "^7.29.7"
+ "@babel/types" "^7.29.7"
+ "@jridgewell/gen-mapping" "^0.3.12"
+ "@jridgewell/trace-mapping" "^0.3.28"
+ jsesc "^3.0.2"
+
+"@babel/helper-compilation-targets@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz#7a1def704302401c47f64fa85589e974ae217042"
+ integrity sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==
+ dependencies:
+ "@babel/compat-data" "^7.29.7"
+ "@babel/helper-validator-option" "^7.29.7"
+ browserslist "^4.24.0"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
+
+"@babel/helper-globals@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz#f04a96fbd8473241b1079243f5b3f03a3010ab7b"
+ integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==
+
+"@babel/helper-module-imports@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz#ef25048a518e828d7393fac5882ddd73921d7396"
+ integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==
+ dependencies:
+ "@babel/traverse" "^7.29.7"
+ "@babel/types" "^7.29.7"
+
+"@babel/helper-module-transforms@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz#b062747a5997ba138637201328bbff77960574ae"
+ integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.29.7"
+ "@babel/helper-validator-identifier" "^7.29.7"
+ "@babel/traverse" "^7.29.7"
+
+"@babel/helper-string-parser@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f"
+ integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==
+
+"@babel/helper-validator-identifier@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2"
+ integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==
+
+"@babel/helper-validator-option@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz#cf315be940213b354eb4abcc0bd01ebe3f73bc2a"
+ integrity sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==
+
+"@babel/helpers@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.7.tgz#45abfde7548997e34376c3e69feb475cffb4a607"
+ integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==
+ dependencies:
+ "@babel/template" "^7.29.7"
+ "@babel/types" "^7.29.7"
+
+"@babel/parser@^7.24.4", "@babel/parser@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.7.tgz#837b87387cbf5ec5530cb634b3c622f68edb9334"
+ integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==
+ dependencies:
+ "@babel/types" "^7.29.7"
+
+"@babel/template@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.29.7.tgz#4d9d4004f645cdd304de958c725162784ecac700"
+ integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==
+ dependencies:
+ "@babel/code-frame" "^7.29.7"
+ "@babel/parser" "^7.29.7"
+ "@babel/types" "^7.29.7"
+
+"@babel/traverse@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.7.tgz#c47b07a41b95da0907d026b5dd894d98de7d2f2d"
+ integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==
+ dependencies:
+ "@babel/code-frame" "^7.29.7"
+ "@babel/generator" "^7.29.7"
+ "@babel/helper-globals" "^7.29.7"
+ "@babel/parser" "^7.29.7"
+ "@babel/template" "^7.29.7"
+ "@babel/types" "^7.29.7"
+ debug "^4.3.1"
+
+"@babel/types@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92"
+ integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==
+ dependencies:
+ "@babel/helper-string-parser" "^7.29.7"
+ "@babel/helper-validator-identifier" "^7.29.7"
+
+"@emnapi/core@1.10.0":
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467"
+ integrity sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==
+ dependencies:
+ "@emnapi/wasi-threads" "1.2.1"
+ tslib "^2.4.0"
+
+"@emnapi/core@^1.10.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.0.tgz#8a655042dbbb10d0266670c9903c34a7001c705b"
+ integrity sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==
+ dependencies:
+ "@emnapi/wasi-threads" "1.2.2"
+ tslib "^2.4.0"
+
+"@emnapi/runtime@1.10.0":
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.10.0.tgz#4b260c0d3534204e98c6110b8db1a987d26ec87c"
+ integrity sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==
+ dependencies:
+ tslib "^2.4.0"
+
+"@emnapi/runtime@^1.10.0":
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.0.tgz#ce16b3674ff7266bbf50f9668bde8a04f3014d4e"
+ integrity sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==
+ dependencies:
+ tslib "^2.4.0"
+
+"@emnapi/wasi-threads@1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz#28fed21a1ba1ce797c44a070abc94d42f3ae8548"
+ integrity sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==
+ dependencies:
+ tslib "^2.4.0"
+
+"@emnapi/wasi-threads@1.2.2", "@emnapi/wasi-threads@^1.2.1":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz#4c93becf5bfa3b13d1bbdcc06aee38321ad8139a"
+ integrity sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==
+ dependencies:
+ tslib "^2.4.0"
+
+"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1":
+ version "4.9.1"
+ resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595"
+ integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==
+ dependencies:
+ eslint-visitor-keys "^3.4.3"
+
+"@eslint-community/regexpp@^4.12.2":
+ version "4.12.2"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
+ integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
+
+"@eslint/config-array@^0.23.5":
+ version "0.23.5"
+ resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.5.tgz#56e86d243049195d8acc0c06a1b3dfdc3fa3de95"
+ integrity sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==
+ dependencies:
+ "@eslint/object-schema" "^3.0.5"
+ debug "^4.3.1"
+ minimatch "^10.2.4"
+
+"@eslint/config-helpers@^0.6.0":
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.6.0.tgz#ef9a36881d39dfd5dbeac22b0da997fabfb08b03"
+ integrity sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==
+ dependencies:
+ "@eslint/core" "^1.2.1"
+
+"@eslint/core@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.2.1.tgz#c1da7cd1b82fa8787f98b5629fb811848a1b63ce"
+ integrity sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==
+ dependencies:
+ "@types/json-schema" "^7.0.15"
+
+"@eslint/js@^10.0.1":
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-10.0.1.tgz#1e8a876f50117af8ab67e47d5ad94d38d6622583"
+ integrity sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==
+
+"@eslint/object-schema@^3.0.5":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091"
+ integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==
+
+"@eslint/plugin-kit@^0.7.2":
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz#4b0962f3f2c7ce8bc98b3ecfe34525c09d2cb729"
+ integrity sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==
+ dependencies:
+ "@eslint/core" "^1.2.1"
+ levn "^0.4.1"
+
+"@humanfs/core@^0.19.2":
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.2.tgz#a8272ca03b2acf492670222b2320b6c421bfde60"
+ integrity sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==
+ dependencies:
+ "@humanfs/types" "^0.15.0"
+
+"@humanfs/node@^0.16.6":
+ version "0.16.8"
+ resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.8.tgz#8f800cccc13f4f8cd3116e2d9c0a94939da3e3ed"
+ integrity sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==
+ dependencies:
+ "@humanfs/core" "^0.19.2"
+ "@humanfs/types" "^0.15.0"
+ "@humanwhocodes/retry" "^0.4.0"
+
+"@humanfs/types@^0.15.0":
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/@humanfs/types/-/types-0.15.0.tgz#f2a09f62012390b2bff3fc6fb248ddec8c09a090"
+ integrity sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==
+
+"@humanwhocodes/module-importer@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2":
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
+ integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
+
+"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
+ integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
+ dependencies:
+ "@jridgewell/sourcemap-codec" "^1.5.0"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/remapping@^2.3.5":
+ version "2.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
+ integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5":
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
+ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
+
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
+ version "0.3.31"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
+ integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@napi-rs/wasm-runtime@^1.1.4":
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz#cccd6ebc40b991dea6936f9126b1b8155b6c4c95"
+ integrity sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==
+ dependencies:
+ "@tybys/wasm-util" "^0.10.2"
+
+"@oxc-project/types@=0.133.0":
+ version "0.133.0"
+ resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.133.0.tgz#2e282ef9e1d26e06b68ccd14b73f310a3b2cf7f8"
+ integrity sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==
+
+"@rolldown/binding-android-arm64@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz#54ce8f8382213f4a314a0c2f7ba83f81ffeae592"
+ integrity sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==
+
+"@rolldown/binding-darwin-arm64@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz#388fca1566c14c00c4b446fc3928630e7f0d95fc"
+ integrity sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==
+
+"@rolldown/binding-darwin-x64@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz#53f57de1f599ecf1db13823cfc88c18fb80954ad"
+ integrity sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==
+
+"@rolldown/binding-freebsd-x64@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz#6f3fdda1b7aeaac9d268a526804b4fb96e4e35f1"
+ integrity sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==
+
+"@rolldown/binding-linux-arm-gnueabihf@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz#d87a454bf585cc9676849377e91d6e375297326f"
+ integrity sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==
+
+"@rolldown/binding-linux-arm64-gnu@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz#419fd6bf612cf348f10528cbcd94ebab9607d8d1"
+ integrity sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==
+
+"@rolldown/binding-linux-arm64-musl@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz#fcc6918696bb76844877e1e4930a18fd0d374069"
+ integrity sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==
+
+"@rolldown/binding-linux-ppc64-gnu@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz#32aecb7c8dae5d4f2a8cde57a058ec86991542f8"
+ integrity sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==
+
+"@rolldown/binding-linux-s390x-gnu@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz#bed9346ea81e6bb8b93cf11f5d88b77db890b763"
+ integrity sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==
+
+"@rolldown/binding-linux-x64-gnu@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz#64c2d26f75dffd9b5a1f97557a00ae77250c8cb7"
+ integrity sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==
+
+"@rolldown/binding-linux-x64-musl@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz#5a45132e8a47659eeaaf3b540c2954a97c860ff3"
+ integrity sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==
+
+"@rolldown/binding-openharmony-arm64@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz#290513068c55e849dc8457a32afee1d7b0acb309"
+ integrity sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==
+
+"@rolldown/binding-wasm32-wasi@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz#3d9972dbf1a953d3c7afaa4a0f20ef2b2e39f31b"
+ integrity sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==
+ dependencies:
+ "@emnapi/core" "1.10.0"
+ "@emnapi/runtime" "1.10.0"
+ "@napi-rs/wasm-runtime" "^1.1.4"
+
+"@rolldown/binding-win32-arm64-msvc@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz#a004ab607a16d6f03bcb555728ff888af75773ad"
+ integrity sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==
+
+"@rolldown/binding-win32-x64-msvc@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz#e2a25b34691a1cc8a1209d7de709063026dd0cdb"
+ integrity sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==
+
+"@rolldown/pluginutils@^1.0.0":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz#e3fcee093fbb5ce765e1ad088ff4de2889f6f9be"
+ integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==
+
+"@tailwindcss/node@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.3.0.tgz#9dc5312bf41c48658529f36021e0b466c4eb7860"
+ integrity sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==
+ dependencies:
+ "@jridgewell/remapping" "^2.3.5"
+ enhanced-resolve "^5.21.0"
+ jiti "^2.6.1"
+ lightningcss "1.32.0"
+ magic-string "^0.30.21"
+ source-map-js "^1.2.1"
+ tailwindcss "4.3.0"
+
+"@tailwindcss/oxide-android-arm64@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz#e4533b6125236fe81a899cf5a82028c85244def8"
+ integrity sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==
+
+"@tailwindcss/oxide-darwin-arm64@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz#96b074ef64ec6c41d580063740c8d36cf5c459ce"
+ integrity sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==
+
+"@tailwindcss/oxide-darwin-x64@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz#0d9638d06d38684339b2dc06631966a7296bb64e"
+ integrity sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==
+
+"@tailwindcss/oxide-freebsd-x64@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz#efc7acd17cd38d7585c07cb938a4f1b703f79d7a"
+ integrity sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==
+
+"@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz#e41c945e529670cd93fd6ed0c6a2880de5c40333"
+ integrity sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==
+
+"@tailwindcss/oxide-linux-arm64-gnu@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz#6bb608b16ba7146d61097c2f4c7ee927d1f3580a"
+ integrity sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==
+
+"@tailwindcss/oxide-linux-arm64-musl@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz#1bb443aa371bb99b50cb39d4d688151fadcd8a63"
+ integrity sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==
+
+"@tailwindcss/oxide-linux-x64-gnu@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz#5267c0bb2597426c0d2e759acb5389cde2aa71fd"
+ integrity sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==
+
+"@tailwindcss/oxide-linux-x64-musl@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz#fb2da97c67b218e5c7c723cb32782d55d7e4a5d5"
+ integrity sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==
+
+"@tailwindcss/oxide-wasm32-wasi@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz#3f6538e511066d67d8683863dcaeeb16c22de849"
+ integrity sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==
+ dependencies:
+ "@emnapi/core" "^1.10.0"
+ "@emnapi/runtime" "^1.10.0"
+ "@emnapi/wasi-threads" "^1.2.1"
+ "@napi-rs/wasm-runtime" "^1.1.4"
+ "@tybys/wasm-util" "^0.10.1"
+ tslib "^2.8.1"
+
+"@tailwindcss/oxide-win32-arm64-msvc@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz#ec45fba773c76759338c05d4fe5cf42c4eea2e4e"
+ integrity sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==
+
+"@tailwindcss/oxide-win32-x64-msvc@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz#58cdd6e06adbe2e3160274edfcd0b0b43e17fee4"
+ integrity sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==
+
+"@tailwindcss/oxide@4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.3.0.tgz#cc1c61e88f62c0e9f56062de3e7873acaa2159d4"
+ integrity sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==
+ optionalDependencies:
+ "@tailwindcss/oxide-android-arm64" "4.3.0"
+ "@tailwindcss/oxide-darwin-arm64" "4.3.0"
+ "@tailwindcss/oxide-darwin-x64" "4.3.0"
+ "@tailwindcss/oxide-freebsd-x64" "4.3.0"
+ "@tailwindcss/oxide-linux-arm-gnueabihf" "4.3.0"
+ "@tailwindcss/oxide-linux-arm64-gnu" "4.3.0"
+ "@tailwindcss/oxide-linux-arm64-musl" "4.3.0"
+ "@tailwindcss/oxide-linux-x64-gnu" "4.3.0"
+ "@tailwindcss/oxide-linux-x64-musl" "4.3.0"
+ "@tailwindcss/oxide-wasm32-wasi" "4.3.0"
+ "@tailwindcss/oxide-win32-arm64-msvc" "4.3.0"
+ "@tailwindcss/oxide-win32-x64-msvc" "4.3.0"
+
+"@tailwindcss/vite@^4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@tailwindcss/vite/-/vite-4.3.0.tgz#b2bbc069a4c700ea7aef5ee30416d84b7652e136"
+ integrity sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==
+ dependencies:
+ "@tailwindcss/node" "4.3.0"
+ "@tailwindcss/oxide" "4.3.0"
+ tailwindcss "4.3.0"
+
+"@tybys/wasm-util@^0.10.1", "@tybys/wasm-util@^0.10.2":
+ version "0.10.2"
+ resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.2.tgz#12b3a1b33db1f9cad4ddff1f604ab7dd00bf464e"
+ integrity sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==
+ dependencies:
+ tslib "^2.4.0"
+
+"@types/esrecurse@^4.3.1":
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec"
+ integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==
+
+"@types/estree@^1.0.6", "@types/estree@^1.0.8":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24"
+ integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==
+
+"@types/json-schema@^7.0.15":
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
+"@types/node@^24.12.3":
+ version "24.13.2"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-24.13.2.tgz#3b9b280a7055128359f125eb1067d9a190f39854"
+ integrity sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==
+ dependencies:
+ undici-types "~7.18.0"
+
+"@types/react-dom@^19.2.3":
+ version "19.2.3"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"
+ integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
+
+"@types/react@^19.2.14":
+ version "19.2.17"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.17.tgz#dccac365baa0f1734ec270ff4b51c89465e8dc7f"
+ integrity sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==
+ dependencies:
+ csstype "^3.2.2"
+
+"@typescript-eslint/eslint-plugin@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz#db20271974b94a3a54d3b9544e5f5b3481448400"
+ integrity sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==
+ dependencies:
+ "@eslint-community/regexpp" "^4.12.2"
+ "@typescript-eslint/scope-manager" "8.61.0"
+ "@typescript-eslint/type-utils" "8.61.0"
+ "@typescript-eslint/utils" "8.61.0"
+ "@typescript-eslint/visitor-keys" "8.61.0"
+ ignore "^7.0.5"
+ natural-compare "^1.4.0"
+ ts-api-utils "^2.5.0"
+
+"@typescript-eslint/parser@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.61.0.tgz#1afe73c9ccce16b7a26d6b95f9400b0ccc34af87"
+ integrity sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==
+ dependencies:
+ "@typescript-eslint/scope-manager" "8.61.0"
+ "@typescript-eslint/types" "8.61.0"
+ "@typescript-eslint/typescript-estree" "8.61.0"
+ "@typescript-eslint/visitor-keys" "8.61.0"
+ debug "^4.4.3"
+
+"@typescript-eslint/project-service@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.61.0.tgz#417a2feac32e8ebd336d63f068c3b42b736ea1ac"
+ integrity sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==
+ dependencies:
+ "@typescript-eslint/tsconfig-utils" "^8.61.0"
+ "@typescript-eslint/types" "^8.61.0"
+ debug "^4.4.3"
+
+"@typescript-eslint/scope-manager@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz#93c2520d05653fe65eb9ee98efc74fd0134a7852"
+ integrity sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==
+ dependencies:
+ "@typescript-eslint/types" "8.61.0"
+ "@typescript-eslint/visitor-keys" "8.61.0"
+
+"@typescript-eslint/tsconfig-utils@8.61.0", "@typescript-eslint/tsconfig-utils@^8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz#05d6e3ff20001674ebcd22d03dac29ee448043ba"
+ integrity sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==
+
+"@typescript-eslint/type-utils@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz#50219b57e6b89cecfb1a15f093b15ec9ee019974"
+ integrity sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==
+ dependencies:
+ "@typescript-eslint/types" "8.61.0"
+ "@typescript-eslint/typescript-estree" "8.61.0"
+ "@typescript-eslint/utils" "8.61.0"
+ debug "^4.4.3"
+ ts-api-utils "^2.5.0"
+
+"@typescript-eslint/types@8.61.0", "@typescript-eslint/types@^8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.61.0.tgz#0ddb46e012a4288292950bdd253db42f278ce64d"
+ integrity sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==
+
+"@typescript-eslint/typescript-estree@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz#98ca47260bbf627fc28f018b3a0abf00e3090690"
+ integrity sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==
+ dependencies:
+ "@typescript-eslint/project-service" "8.61.0"
+ "@typescript-eslint/tsconfig-utils" "8.61.0"
+ "@typescript-eslint/types" "8.61.0"
+ "@typescript-eslint/visitor-keys" "8.61.0"
+ debug "^4.4.3"
+ minimatch "^10.2.2"
+ semver "^7.7.3"
+ tinyglobby "^0.2.15"
+ ts-api-utils "^2.5.0"
+
+"@typescript-eslint/utils@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.61.0.tgz#ed3546a052787e84ea6c5064d0919fc5eea8522f"
+ integrity sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.9.1"
+ "@typescript-eslint/scope-manager" "8.61.0"
+ "@typescript-eslint/types" "8.61.0"
+ "@typescript-eslint/typescript-estree" "8.61.0"
+
+"@typescript-eslint/visitor-keys@8.61.0":
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz#39b4e1ab8936d23bea973d39fd092f9aa21f275e"
+ integrity sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==
+ dependencies:
+ "@typescript-eslint/types" "8.61.0"
+ eslint-visitor-keys "^5.0.0"
+
+"@vitejs/plugin-react@^6.0.1":
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz#f70cb8ed0ce225dbc3055d78070f820d8aa35eda"
+ integrity sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==
+ dependencies:
+ "@rolldown/pluginutils" "^1.0.0"
+
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.16.0:
+ version "8.17.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.17.0.tgz#1785adb84faf8d8add10369b93826fc2bd08f1fe"
+ integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==
+
+agent-base@6:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+ integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
+ dependencies:
+ debug "4"
+
+ajv@^6.14.0:
+ version "6.15.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492"
+ integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^1.17.0:
+ version "1.17.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.17.0.tgz#ae5a1164a4f719942cd73c67e6a3f62d3ccb8f2b"
+ integrity sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==
+ dependencies:
+ follow-redirects "^1.16.0"
+ form-data "^4.0.5"
+ https-proxy-agent "^5.0.1"
+ proxy-from-env "^2.1.0"
+
+balanced-match@^4.0.2:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
+ integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
+
+baseline-browser-mapping@^2.10.12:
+ version "2.10.36"
+ resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.36.tgz#ff3c7c87095c70075b1ac787160fd4fc98750a74"
+ integrity sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==
+
+brace-expansion@^5.0.5:
+ version "5.0.6"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285"
+ integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==
+ dependencies:
+ balanced-match "^4.0.2"
+
+browserslist@^4.24.0:
+ version "4.28.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2"
+ integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==
+ dependencies:
+ baseline-browser-mapping "^2.10.12"
+ caniuse-lite "^1.0.30001782"
+ electron-to-chromium "^1.5.328"
+ node-releases "^2.0.36"
+ update-browserslist-db "^1.2.3"
+
+call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
+ integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+
+caniuse-lite@^1.0.30001782:
+ version "1.0.30001799"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz#5c909138c27f1a61219d3e092071c1cc7d32dc55"
+ integrity sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==
+
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+convert-source-map@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+ integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
+cookie@^1.0.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c"
+ integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==
+
+cross-spawn@^7.0.6:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+ integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+csstype@^3.2.2:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
+ integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
+
+debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.4.3:
+ version "4.4.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
+ integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
+ dependencies:
+ ms "^2.1.3"
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+detect-libc@^2.0.3:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
+ integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
+
+dunder-proto@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
+ integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
+ dependencies:
+ call-bind-apply-helpers "^1.0.1"
+ es-errors "^1.3.0"
+ gopd "^1.2.0"
+
+electron-to-chromium@^1.5.328:
+ version "1.5.372"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz#ae8ac69942a37b231773b8fb01759f73733599e3"
+ integrity sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==
+
+enhanced-resolve@^5.21.0:
+ version "5.24.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz#cf14b9768a774cb6a5087220c0dc6e55df6ec35a"
+ integrity sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.3.3"
+
+es-define-property@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
+ integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
+
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b"
+ integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==
+ dependencies:
+ es-errors "^1.3.0"
+
+es-set-tostringtag@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
+ integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
+ dependencies:
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.6"
+ has-tostringtag "^1.0.2"
+ hasown "^2.0.2"
+
+escalade@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-plugin-react-hooks@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz#e6742cad75d970c0a3f30d7d3fa80a4784f55927"
+ integrity sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==
+ dependencies:
+ "@babel/core" "^7.24.4"
+ "@babel/parser" "^7.24.4"
+ hermes-parser "^0.25.1"
+ zod "^3.25.0 || ^4.0.0"
+ zod-validation-error "^3.5.0 || ^4.0.0"
+
+eslint-plugin-react-refresh@^0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz#39e11021be10e1cd9adab2bdeabc65b17222409f"
+ integrity sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==
+
+eslint-scope@^9.1.2:
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-9.1.2.tgz#b9de6ace2fab1cff24d2e58d85b74c8fcea39802"
+ integrity sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==
+ dependencies:
+ "@types/esrecurse" "^4.3.1"
+ "@types/estree" "^1.0.8"
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-visitor-keys@^3.4.3:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+ integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be"
+ integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==
+
+eslint@^10.3.0:
+ version "10.4.1"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.4.1.tgz#f6640b176e0912246d9ddbf8fcfa5e8b7f02445a"
+ integrity sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.8.0"
+ "@eslint-community/regexpp" "^4.12.2"
+ "@eslint/config-array" "^0.23.5"
+ "@eslint/config-helpers" "^0.6.0"
+ "@eslint/core" "^1.2.1"
+ "@eslint/plugin-kit" "^0.7.2"
+ "@humanfs/node" "^0.16.6"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@humanwhocodes/retry" "^0.4.2"
+ "@types/estree" "^1.0.6"
+ ajv "^6.14.0"
+ cross-spawn "^7.0.6"
+ debug "^4.3.2"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^9.1.2"
+ eslint-visitor-keys "^5.0.1"
+ espree "^11.2.0"
+ esquery "^1.7.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^8.0.0"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ ignore "^5.2.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ minimatch "^10.2.4"
+ natural-compare "^1.4.0"
+ optionator "^0.9.3"
+
+espree@^11.2.0:
+ version "11.2.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-11.2.0.tgz#01d5e47dc332aaba3059008362454a8cc34ccaa5"
+ integrity sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==
+ dependencies:
+ acorn "^8.16.0"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^5.0.1"
+
+esquery@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d"
+ integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fdir@^6.5.0:
+ version "6.5.0"
+ resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
+ integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
+
+file-entry-cache@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
+ integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
+ dependencies:
+ flat-cache "^4.0.0"
+
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
+flat-cache@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
+ integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
+ dependencies:
+ flatted "^3.2.9"
+ keyv "^4.5.4"
+
+flatted@^3.2.9:
+ version "3.4.2"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
+ integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
+
+follow-redirects@^1.16.0:
+ version "1.16.0"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc"
+ integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==
+
+form-data@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053"
+ integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ es-set-tostringtag "^2.1.0"
+ hasown "^2.0.2"
+ mime-types "^2.1.12"
+
+fsevents@~2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+get-intrinsic@^1.2.6:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
+ integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
+ dependencies:
+ call-bind-apply-helpers "^1.0.2"
+ es-define-property "^1.0.1"
+ es-errors "^1.3.0"
+ es-object-atoms "^1.1.1"
+ function-bind "^1.1.2"
+ get-proto "^1.0.1"
+ gopd "^1.2.0"
+ has-symbols "^1.1.0"
+ hasown "^2.0.2"
+ math-intrinsics "^1.1.0"
+
+get-proto@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
+ integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
+ dependencies:
+ dunder-proto "^1.0.1"
+ es-object-atoms "^1.0.0"
+
+glob-parent@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+globals@^17.6.0:
+ version "17.6.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-17.6.0.tgz#0f0be018d5cca8690e6375ead1f65c4bb96191fc"
+ integrity sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==
+
+gopd@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
+ integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+
+graceful-fs@^4.2.4:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+has-symbols@^1.0.3, has-symbols@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
+ integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
+
+has-tostringtag@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
+ integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
+ dependencies:
+ has-symbols "^1.0.3"
+
+hasown@^2.0.2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003"
+ integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==
+ dependencies:
+ function-bind "^1.1.2"
+
+hermes-estree@0.25.1:
+ version "0.25.1"
+ resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
+ integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==
+
+hermes-parser@^0.25.1:
+ version "0.25.1"
+ resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1"
+ integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==
+ dependencies:
+ hermes-estree "0.25.1"
+
+https-proxy-agent@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+ dependencies:
+ agent-base "6"
+ debug "4"
+
+ignore@^5.2.0:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+ integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+
+ignore@^7.0.5:
+ version "7.0.5"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9"
+ integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.0, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+jiti@^2.6.1:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.7.0.tgz#974228f2f4ca2bc21885a1797b45fea68e950c64"
+ integrity sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==
+
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+jsesc@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
+ integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
+
+json-buffer@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+ integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+json5@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+keyv@^4.5.4:
+ version "4.5.4"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+ integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+ dependencies:
+ json-buffer "3.0.1"
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+lightningcss-android-arm64@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968"
+ integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==
+
+lightningcss-darwin-arm64@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5"
+ integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==
+
+lightningcss-darwin-x64@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e"
+ integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==
+
+lightningcss-freebsd-x64@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575"
+ integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==
+
+lightningcss-linux-arm-gnueabihf@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d"
+ integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==
+
+lightningcss-linux-arm64-gnu@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335"
+ integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==
+
+lightningcss-linux-arm64-musl@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133"
+ integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==
+
+lightningcss-linux-x64-gnu@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6"
+ integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==
+
+lightningcss-linux-x64-musl@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b"
+ integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==
+
+lightningcss-win32-arm64-msvc@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38"
+ integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==
+
+lightningcss-win32-x64-msvc@1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a"
+ integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==
+
+lightningcss@1.32.0, lightningcss@^1.32.0:
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9"
+ integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==
+ dependencies:
+ detect-libc "^2.0.3"
+ optionalDependencies:
+ lightningcss-android-arm64 "1.32.0"
+ lightningcss-darwin-arm64 "1.32.0"
+ lightningcss-darwin-x64 "1.32.0"
+ lightningcss-freebsd-x64 "1.32.0"
+ lightningcss-linux-arm-gnueabihf "1.32.0"
+ lightningcss-linux-arm64-gnu "1.32.0"
+ lightningcss-linux-arm64-musl "1.32.0"
+ lightningcss-linux-x64-gnu "1.32.0"
+ lightningcss-linux-x64-musl "1.32.0"
+ lightningcss-win32-arm64-msvc "1.32.0"
+ lightningcss-win32-x64-msvc "1.32.0"
+
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
+lru-cache@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+ integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+ dependencies:
+ yallist "^3.0.2"
+
+lucide-react@^1.18.0:
+ version "1.18.0"
+ resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-1.18.0.tgz#d1b8754d9c427061261c6a6a0b7fa6ecda36c84f"
+ integrity sha512-LZDb7H/0YfM+RJncD0hDQRCAu+vSGODqpe35TuVI8EuXaRjkczbsx7p8dY4J87F/MUSj6bpYqeI8nw8qXaAdmA==
+
+magic-string@^0.30.21:
+ version "0.30.21"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91"
+ integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==
+ dependencies:
+ "@jridgewell/sourcemap-codec" "^1.5.5"
+
+math-intrinsics@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
+ integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+minimatch@^10.2.2, minimatch@^10.2.4:
+ version "10.2.5"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
+ integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==
+ dependencies:
+ brace-expansion "^5.0.5"
+
+ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+nanoid@^3.3.12:
+ version "3.3.12"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.12.tgz#ab3d912e217a6d0a514f00a72a16543a28982c05"
+ integrity sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+node-releases@^2.0.36:
+ version "2.0.47"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.47.tgz#521bb2786da8eb140b748841c0b3b3a75334ffc4"
+ integrity sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==
+
+optionator@^0.9.3:
+ version "0.9.4"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
+ integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.5"
+
+p-limit@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
+ integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
+
+postcss@^8.5.15:
+ version "8.5.15"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.15.tgz#d1eaf677a324e9ec02196da2d3fecf4a0b9a735c"
+ integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==
+ dependencies:
+ nanoid "^3.3.12"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+proxy-from-env@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba"
+ integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==
+
+punycode@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+react-dom@^19.2.6:
+ version "19.2.7"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.7.tgz#0450dc9ae9ddbff76ef196401cd8b8c7fb466ccc"
+ integrity sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==
+ dependencies:
+ scheduler "^0.27.0"
+
+react-icons@^5.6.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.6.0.tgz#27bcc4acbc836e762548d76041cf9b9fef4e3837"
+ integrity sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==
+
+react-router-dom@^7.17.0:
+ version "7.17.0"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.17.0.tgz#e77527b4b7862f7b47ff26dd5b9315fb897b82a7"
+ integrity sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw==
+ dependencies:
+ react-router "7.17.0"
+
+react-router@7.17.0:
+ version "7.17.0"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.17.0.tgz#88bbe817c6e37ab36faf140623b5d4678bf81e41"
+ integrity sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ==
+ dependencies:
+ cookie "^1.0.1"
+ set-cookie-parser "^2.6.0"
+
+react@^19.2.6:
+ version "19.2.7"
+ resolved "https://registry.yarnpkg.com/react/-/react-19.2.7.tgz#1f47a1bfc06f8ec885752c6f4af14369a9f8260b"
+ integrity sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==
+
+rolldown@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.3.tgz#db88a3008fb0e28230a00423727ce75ba32121ac"
+ integrity sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==
+ dependencies:
+ "@oxc-project/types" "=0.133.0"
+ "@rolldown/pluginutils" "^1.0.0"
+ optionalDependencies:
+ "@rolldown/binding-android-arm64" "1.0.3"
+ "@rolldown/binding-darwin-arm64" "1.0.3"
+ "@rolldown/binding-darwin-x64" "1.0.3"
+ "@rolldown/binding-freebsd-x64" "1.0.3"
+ "@rolldown/binding-linux-arm-gnueabihf" "1.0.3"
+ "@rolldown/binding-linux-arm64-gnu" "1.0.3"
+ "@rolldown/binding-linux-arm64-musl" "1.0.3"
+ "@rolldown/binding-linux-ppc64-gnu" "1.0.3"
+ "@rolldown/binding-linux-s390x-gnu" "1.0.3"
+ "@rolldown/binding-linux-x64-gnu" "1.0.3"
+ "@rolldown/binding-linux-x64-musl" "1.0.3"
+ "@rolldown/binding-openharmony-arm64" "1.0.3"
+ "@rolldown/binding-wasm32-wasi" "1.0.3"
+ "@rolldown/binding-win32-arm64-msvc" "1.0.3"
+ "@rolldown/binding-win32-x64-msvc" "1.0.3"
+
+scheduler@^0.27.0:
+ version "0.27.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
+ integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
+
+semver@^6.3.1:
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.7.3:
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.4.tgz#c73eceebae0616934be8dff28a7fd70757c8e696"
+ integrity sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==
+
+set-cookie-parser@^2.6.0:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68"
+ integrity sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+source-map-js@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+ integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+tailwindcss@4.3.0, tailwindcss@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.3.0.tgz#0a874e044a859cf6de413f3a59e76a9bedf05264"
+ integrity sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==
+
+tapable@^2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.3.tgz#5da7c9992c46038221267985ab28421a8879f160"
+ integrity sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==
+
+tinyglobby@^0.2.15, tinyglobby@^0.2.17:
+ version "0.2.17"
+ resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631"
+ integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==
+ dependencies:
+ fdir "^6.5.0"
+ picomatch "^4.0.4"
+
+ts-api-utils@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1"
+ integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==
+
+tslib@^2.4.0, tslib@^2.8.1:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+ integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+typescript-eslint@^8.59.2:
+ version "8.61.0"
+ resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.61.0.tgz#6927fb94f5f29623e370d33fd9fa61f15d6d996b"
+ integrity sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==
+ dependencies:
+ "@typescript-eslint/eslint-plugin" "8.61.0"
+ "@typescript-eslint/parser" "8.61.0"
+ "@typescript-eslint/typescript-estree" "8.61.0"
+ "@typescript-eslint/utils" "8.61.0"
+
+typescript@~6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21"
+ integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==
+
+undici-types@~7.18.0:
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9"
+ integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==
+
+update-browserslist-db@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d"
+ integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==
+ dependencies:
+ escalade "^3.2.0"
+ picocolors "^1.1.1"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+vite@^8.0.12:
+ version "8.0.16"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-8.0.16.tgz#ae073866c06563d6634a90169a496e11bd84f1a6"
+ integrity sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==
+ dependencies:
+ lightningcss "^1.32.0"
+ picomatch "^4.0.4"
+ postcss "^8.5.15"
+ rolldown "1.0.3"
+ tinyglobby "^0.2.17"
+ optionalDependencies:
+ fsevents "~2.3.3"
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+word-wrap@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
+ integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+
+yallist@^3.0.2:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+ integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+"zod-validation-error@^3.5.0 || ^4.0.0":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.2.tgz#bc605eba49ce0fcd598c127fee1c236be3f22918"
+ integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==
+
+"zod@^3.25.0 || ^4.0.0":
+ version "4.4.3"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356"
+ integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==
Connect with us
-Join the Vite community
---
-
-
- GitHub
-
-
- -
-
-
- Discord
-
-
- -
-
-
- X.com
-
-
- -
-
-
- Bluesky
-
-
-
-