diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..958d068c
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "arrowParens": "always",
+ "bracketSpacing": true,
+ "endOfLine": "lf",
+ "printWidth": 120,
+ "semi": true,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "trailingComma": "all",
+ "useTabs": false,
+ "plugins": ["prettier-plugin-tailwindcss"]
+}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..b19330b1
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,23 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs['recommended-latest'],
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..02b51475
--- /dev/null
+++ b/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+ react-messenger
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..b1289558
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4283 @@
+{
+ "name": "react-messenger",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "react-messenger",
+ "version": "0.0.0",
+ "dependencies": {
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "react-router-dom": "^7.9.4",
+ "react-spinners": "^0.17.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@tailwindcss/postcss": "^4.1.13",
+ "@tailwindcss/vite": "^4.1.13",
+ "@types/react": "^19.1.13",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.3",
+ "eslint": "^9.36.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.20",
+ "globals": "^16.4.0",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
+ "tailwindcss": "^4.1.13",
+ "typescript": "~5.8.3",
+ "typescript-eslint": "^8.44.0",
+ "vite": "^7.1.7"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
+ "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
+ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.4",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.4",
+ "@babel/types": "^7.28.4",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
+ "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.4"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
+ "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.4",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
+ "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz",
+ "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz",
+ "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz",
+ "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz",
+ "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz",
+ "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz",
+ "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz",
+ "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz",
+ "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz",
+ "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz",
+ "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz",
+ "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz",
+ "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz",
+ "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz",
+ "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz",
+ "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz",
+ "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz",
+ "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz",
+ "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz",
+ "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz",
+ "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz",
+ "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz",
+ "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz",
+ "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz",
+ "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz",
+ "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz",
+ "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
+ "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
+ "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
+ "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.36.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
+ "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
+ "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.15.2",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.35",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.35.tgz",
+ "integrity": "sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.2.tgz",
+ "integrity": "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.2.tgz",
+ "integrity": "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.2.tgz",
+ "integrity": "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.2.tgz",
+ "integrity": "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.2.tgz",
+ "integrity": "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.2.tgz",
+ "integrity": "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.2.tgz",
+ "integrity": "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.2.tgz",
+ "integrity": "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.2.tgz",
+ "integrity": "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.2.tgz",
+ "integrity": "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.2.tgz",
+ "integrity": "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.2.tgz",
+ "integrity": "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.2.tgz",
+ "integrity": "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.2.tgz",
+ "integrity": "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.2.tgz",
+ "integrity": "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.2.tgz",
+ "integrity": "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.2.tgz",
+ "integrity": "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.2.tgz",
+ "integrity": "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.2.tgz",
+ "integrity": "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.2.tgz",
+ "integrity": "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.2.tgz",
+ "integrity": "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz",
+ "integrity": "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
+ "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.5.1",
+ "lightningcss": "1.30.1",
+ "magic-string": "^0.30.18",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.13"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz",
+ "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.13",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.13",
+ "@tailwindcss/oxide-darwin-x64": "4.1.13",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.13",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.13",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.13",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.13",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.13",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.13"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz",
+ "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz",
+ "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz",
+ "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz",
+ "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz",
+ "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz",
+ "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz",
+ "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz",
+ "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz",
+ "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz",
+ "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.5",
+ "@emnapi/runtime": "^1.4.5",
+ "@emnapi/wasi-threads": "^1.0.4",
+ "@napi-rs/wasm-runtime": "^0.2.12",
+ "@tybys/wasm-util": "^0.10.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz",
+ "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz",
+ "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz",
+ "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.13",
+ "@tailwindcss/oxide": "4.1.13",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.13"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz",
+ "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.1.13",
+ "@tailwindcss/oxide": "4.1.13",
+ "tailwindcss": "4.1.13"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.13",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz",
+ "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.9",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
+ "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz",
+ "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.44.1",
+ "@typescript-eslint/type-utils": "8.44.1",
+ "@typescript-eslint/utils": "8.44.1",
+ "@typescript-eslint/visitor-keys": "8.44.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.44.1",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz",
+ "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.44.1",
+ "@typescript-eslint/types": "8.44.1",
+ "@typescript-eslint/typescript-estree": "8.44.1",
+ "@typescript-eslint/visitor-keys": "8.44.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz",
+ "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.44.1",
+ "@typescript-eslint/types": "^8.44.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz",
+ "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.44.1",
+ "@typescript-eslint/visitor-keys": "8.44.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz",
+ "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz",
+ "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.44.1",
+ "@typescript-eslint/typescript-estree": "8.44.1",
+ "@typescript-eslint/utils": "8.44.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz",
+ "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz",
+ "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.44.1",
+ "@typescript-eslint/tsconfig-utils": "8.44.1",
+ "@typescript-eslint/types": "8.44.1",
+ "@typescript-eslint/visitor-keys": "8.44.1",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz",
+ "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.44.1",
+ "@typescript-eslint/types": "8.44.1",
+ "@typescript-eslint/typescript-estree": "8.44.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz",
+ "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.44.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz",
+ "integrity": "sha512-PFVHhosKkofGH0Yzrw1BipSedTH68BFF8ZWy1kfUpCtJcouXXY0+racG8sExw7hw0HoX36813ga5o3LTWZ4FUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.4",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.35",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz",
+ "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.26.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz",
+ "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.8.3",
+ "caniuse-lite": "^1.0.30001741",
+ "electron-to-chromium": "^1.5.218",
+ "node-releases": "^2.0.21",
+ "update-browserslist-db": "^1.1.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001743",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz",
+ "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz",
+ "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.223",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.223.tgz",
+ "integrity": "sha512-qKm55ic6nbEmagFlTFczML33rF90aU+WtrJ9MdTCThrcvDNdUHN4p6QfVN78U06ZmguqXIyMPyYhw2TrbDUwPQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.3",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
+ "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz",
+ "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.10",
+ "@esbuild/android-arm": "0.25.10",
+ "@esbuild/android-arm64": "0.25.10",
+ "@esbuild/android-x64": "0.25.10",
+ "@esbuild/darwin-arm64": "0.25.10",
+ "@esbuild/darwin-x64": "0.25.10",
+ "@esbuild/freebsd-arm64": "0.25.10",
+ "@esbuild/freebsd-x64": "0.25.10",
+ "@esbuild/linux-arm": "0.25.10",
+ "@esbuild/linux-arm64": "0.25.10",
+ "@esbuild/linux-ia32": "0.25.10",
+ "@esbuild/linux-loong64": "0.25.10",
+ "@esbuild/linux-mips64el": "0.25.10",
+ "@esbuild/linux-ppc64": "0.25.10",
+ "@esbuild/linux-riscv64": "0.25.10",
+ "@esbuild/linux-s390x": "0.25.10",
+ "@esbuild/linux-x64": "0.25.10",
+ "@esbuild/netbsd-arm64": "0.25.10",
+ "@esbuild/netbsd-x64": "0.25.10",
+ "@esbuild/openbsd-arm64": "0.25.10",
+ "@esbuild/openbsd-x64": "0.25.10",
+ "@esbuild/openharmony-arm64": "0.25.10",
+ "@esbuild/sunos-x64": "0.25.10",
+ "@esbuild/win32-arm64": "0.25.10",
+ "@esbuild/win32-ia32": "0.25.10",
+ "@esbuild/win32-x64": "0.25.10"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.36.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
+ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.3.1",
+ "@eslint/core": "^0.15.2",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.36.0",
+ "@eslint/plugin-kit": "^0.3.5",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.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",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.21",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.21.tgz",
+ "integrity": "sha512-MWDWTtNC4voTcWDxXbdmBNe8b/TxfxRFUL6hXgKWJjN9c1AagYEmpiFWBWzDw+5H3SulWUe1pJKTnoSdmk88UA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz",
+ "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==",
+ "dev": true,
+ "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",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
+ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.30.1",
+ "lightningcss-darwin-x64": "1.30.1",
+ "lightningcss-freebsd-x64": "1.30.1",
+ "lightningcss-linux-arm-gnueabihf": "1.30.1",
+ "lightningcss-linux-arm64-gnu": "1.30.1",
+ "lightningcss-linux-arm64-musl": "1.30.1",
+ "lightningcss-linux-x64-gnu": "1.30.1",
+ "lightningcss-linux-x64-musl": "1.30.1",
+ "lightningcss-win32-arm64-msvc": "1.30.1",
+ "lightningcss-win32-x64-msvc": "1.30.1"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
+ "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
+ "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
+ "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
+ "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
+ "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
+ "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
+ "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
+ "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
+ "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
+ "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.19",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
+ "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.21",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz",
+ "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-plugin-tailwindcss": {
+ "version": "0.6.14",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz",
+ "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "peerDependencies": {
+ "@ianvs/prettier-plugin-sort-imports": "*",
+ "@prettier/plugin-hermes": "*",
+ "@prettier/plugin-oxc": "*",
+ "@prettier/plugin-pug": "*",
+ "@shopify/prettier-plugin-liquid": "*",
+ "@trivago/prettier-plugin-sort-imports": "*",
+ "@zackad/prettier-plugin-twig": "*",
+ "prettier": "^3.0",
+ "prettier-plugin-astro": "*",
+ "prettier-plugin-css-order": "*",
+ "prettier-plugin-import-sort": "*",
+ "prettier-plugin-jsdoc": "*",
+ "prettier-plugin-marko": "*",
+ "prettier-plugin-multiline-arrays": "*",
+ "prettier-plugin-organize-attributes": "*",
+ "prettier-plugin-organize-imports": "*",
+ "prettier-plugin-sort-imports": "*",
+ "prettier-plugin-style-order": "*",
+ "prettier-plugin-svelte": "*"
+ },
+ "peerDependenciesMeta": {
+ "@ianvs/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@prettier/plugin-hermes": {
+ "optional": true
+ },
+ "@prettier/plugin-oxc": {
+ "optional": true
+ },
+ "@prettier/plugin-pug": {
+ "optional": true
+ },
+ "@shopify/prettier-plugin-liquid": {
+ "optional": true
+ },
+ "@trivago/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@zackad/prettier-plugin-twig": {
+ "optional": true
+ },
+ "prettier-plugin-astro": {
+ "optional": true
+ },
+ "prettier-plugin-css-order": {
+ "optional": true
+ },
+ "prettier-plugin-import-sort": {
+ "optional": true
+ },
+ "prettier-plugin-jsdoc": {
+ "optional": true
+ },
+ "prettier-plugin-marko": {
+ "optional": true
+ },
+ "prettier-plugin-multiline-arrays": {
+ "optional": true
+ },
+ "prettier-plugin-organize-attributes": {
+ "optional": true
+ },
+ "prettier-plugin-organize-imports": {
+ "optional": true
+ },
+ "prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "prettier-plugin-style-order": {
+ "optional": true
+ },
+ "prettier-plugin-svelte": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.1.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
+ "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
+ "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.9.4",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz",
+ "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==",
+ "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.9.4",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.4.tgz",
+ "integrity": "sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.9.4"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-spinners": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.17.0.tgz",
+ "integrity": "sha512-L/8HTylaBmIWwQzIjMq+0vyaRXuoAevzWoD35wKpNTxxtYXWZp+xtgkfD7Y4WItuX0YvdxMPU79+7VhhmbmuTQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.52.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz",
+ "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.52.2",
+ "@rollup/rollup-android-arm64": "4.52.2",
+ "@rollup/rollup-darwin-arm64": "4.52.2",
+ "@rollup/rollup-darwin-x64": "4.52.2",
+ "@rollup/rollup-freebsd-arm64": "4.52.2",
+ "@rollup/rollup-freebsd-x64": "4.52.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.52.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.52.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.52.2",
+ "@rollup/rollup-linux-arm64-musl": "4.52.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.52.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.52.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.52.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.52.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.52.2",
+ "@rollup/rollup-linux-x64-gnu": "4.52.2",
+ "@rollup/rollup-linux-x64-musl": "4.52.2",
+ "@rollup/rollup-openharmony-arm64": "4.52.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.52.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.52.2",
+ "@rollup/rollup-win32-x64-gnu": "4.52.2",
+ "@rollup/rollup-win32-x64-msvc": "4.52.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
+ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
+ "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
+ "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz",
+ "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.44.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz",
+ "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.44.1",
+ "@typescript-eslint/parser": "8.44.1",
+ "@typescript-eslint/typescript-estree": "8.44.1",
+ "@typescript-eslint/utils": "8.44.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",
+ "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..cc840dd5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "react-messenger",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "react-router-dom": "^7.9.4",
+ "react-spinners": "^0.17.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@tailwindcss/postcss": "^4.1.13",
+ "@tailwindcss/vite": "^4.1.13",
+ "@types/react": "^19.1.13",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.3",
+ "eslint": "^9.36.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.20",
+ "globals": "^16.4.0",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
+ "tailwindcss": "^4.1.13",
+ "typescript": "~5.8.3",
+ "typescript-eslint": "^8.44.0",
+ "vite": "^7.1.7"
+ }
+}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 00000000..3ce2a83f
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+// postcss.config.js
+export default {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ },
+};
diff --git a/public/ts.svg b/public/ts.svg
new file mode 100644
index 00000000..511d56af
--- /dev/null
+++ b/public/ts.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/App.css b/src/App.css
new file mode 100644
index 00000000..b9d355df
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 00000000..bc89855e
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,91 @@
+// src/App.tsx
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import Home from '@/pages/Home';
+import Chats from '@/pages/Chats';
+import Call from '@/pages/Call';
+import More from '@/pages/More';
+import ChatRoom from '@/pages/ChatRoom';
+import MyProfile from '@/pages/MyProfile';
+import FriendProfile from '@/pages/FriendProfile';
+import MobileFrame from '@/layouts/MobileFrame';
+import ErrorBoundary from '@/components/dev/ErrorBoundary';
+
+export default function App() {
+ return (
+
+
+
+ {/* 홈 - 흰색 하단바 */}
+
+
+
+ }
+ />
+
+ {/* 채팅 목록 - 흰색 하단바 */}
+
+
+
+ }
+ />
+
+ {/* 전화 - 흰색 하단바 */}
+
+
+
+ }
+ />
+
+ {/* 더보기 - 흰색 하단바 */}
+
+
+
+ }
+ />
+
+ {/* 채팅방 - 흰색 하단바 */}
+
+
+
+ }
+ />
+
+ {/* 내 프로필 - 투명 하단바 */}
+
+
+
+ }
+ />
+
+ {/* 친구 프로필 - 투명 하단바 */}
+
+
+
+ }
+ />
+
+
+
+ );
+}
diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx
new file mode 100644
index 00000000..0dcc3b42
--- /dev/null
+++ b/src/app/AppShell.tsx
@@ -0,0 +1,9 @@
+export default function AppShell({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/app/BottomIndicator.tsx b/src/app/BottomIndicator.tsx
new file mode 100644
index 00000000..81026a19
--- /dev/null
+++ b/src/app/BottomIndicator.tsx
@@ -0,0 +1,16 @@
+// src/app/BottomIndicator.tsx
+export default function BottomIndicator() {
+ return (
+
+ {/* 배경 투명 - 부모의 배경색 상속 */}
+
+
+ );
+}
diff --git a/src/app/HeaderBar.tsx b/src/app/HeaderBar.tsx
new file mode 100644
index 00000000..50ee0119
--- /dev/null
+++ b/src/app/HeaderBar.tsx
@@ -0,0 +1,29 @@
+// src/app/HeaderBar.tsx
+import { Icon } from '@/components/Icon';
+import { useNavigate } from 'react-router-dom';
+
+export default function HeaderBar({ title, onBack }: { title: string; onBack?: () => void }) {
+ const nav = useNavigate();
+
+ return (
+
+
+
+
+
+
{title}
+ 65
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/StatusBar.tsx b/src/app/StatusBar.tsx
new file mode 100644
index 00000000..1d809c8f
--- /dev/null
+++ b/src/app/StatusBar.tsx
@@ -0,0 +1,32 @@
+// src/app/StatusBar.tsx
+import { Icon } from '@/components/Icon';
+
+type Props = {
+ theme?: 'dark' | 'light';
+};
+
+export default function StatusBar({ theme = 'dark' }: Props) {
+ const textColor = theme === 'light' ? 'text-white' : 'text-black';
+
+ return (
+
+ {/* 왼쪽: 시간 */}
+
+ {new Date().toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', hour12: false })}
+
+
+ {/* 오른쪽: 아이콘들 */}
+
+
+ );
+}
diff --git a/src/app/TabBar.tsx b/src/app/TabBar.tsx
new file mode 100644
index 00000000..4c1d0ce5
--- /dev/null
+++ b/src/app/TabBar.tsx
@@ -0,0 +1,27 @@
+import { NavLink } from 'react-router-dom';
+import { Icon } from '@/components/Icon';
+
+const tab = 'flex flex-col items-center justify-center gap-1 flex-1 py-2 text-caption';
+
+export default function TabBar() {
+ return (
+
+ );
+}
diff --git a/src/assets/react.svg b/src/assets/react.svg
new file mode 100644
index 00000000..6c87de9b
--- /dev/null
+++ b/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx
new file mode 100644
index 00000000..7bff17cd
--- /dev/null
+++ b/src/components/Icon.tsx
@@ -0,0 +1,32 @@
+// src/components/Icon.tsx
+
+// 상대 글롭: "../icons/**/*.svg"
+const files = import.meta.glob<{ default: string }>('../icons/**/*.svg', { eager: true, import: 'default' });
+
+// 요청/파일명을 안전 키로 변환
+const slug = (s: string) =>
+ s
+ .toLowerCase()
+ .replace(/\s+/g, '-')
+ .replace(/[^a-z0-9.\-]/g, '')
+ .replace(/-+/g, '-');
+
+// 파일명(.svg 제거) → URL
+const registry: Record = {};
+for (const fullPath in files) {
+ const url = files[fullPath] as unknown as string;
+ const file = fullPath.split('/').pop()!; // e.g. "add-user.svg"
+ const key = slug(file.replace(/\.svg$/i, '')); // e.g. "add-user"
+ registry[key] = url;
+}
+
+export function Icon({ name, className, alt }: { name: string; className?: string; alt?: string }) {
+ const src = registry[slug(name)];
+ if (!src)
+ return (
+
+ □
+
+ );
+ return
;
+}
diff --git a/src/components/NowClock.tsx b/src/components/NowClock.tsx
new file mode 100644
index 00000000..fed6321e
--- /dev/null
+++ b/src/components/NowClock.tsx
@@ -0,0 +1,37 @@
+// src/components/NowClock.tsx
+import { useEffect, useState, useRef } from 'react';
+
+type Props = {
+ refreshMs?: number; // 기본 30초
+ className?: string;
+ format?: (d: Date) => string; // 기본: HH:MM (초 없음)
+};
+
+export default function NowClock({
+ refreshMs = 30_000,
+ className,
+ format = (d) => d.toLocaleTimeString('ko-KR', { hour12: false, hour: '2-digit', minute: '2-digit' }),
+}: Props) {
+ const [now, setNow] = useState(new Date());
+ const intervalRef = useRef(null);
+ const timeoutRef = useRef(null);
+
+ useEffect(() => {
+ // :00 / :30 경계에 정확히 맞춰 시작 → 이후 30초마다 갱신
+ const n = new Date();
+ const msInto = (n.getSeconds() * 1000 + n.getMilliseconds()) % refreshMs;
+ const delay = refreshMs - msInto;
+
+ timeoutRef.current = window.setTimeout(() => {
+ setNow(new Date());
+ intervalRef.current = window.setInterval(() => setNow(new Date()), refreshMs);
+ }, delay);
+
+ return () => {
+ if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
+ if (intervalRef.current) window.clearInterval(intervalRef.current);
+ };
+ }, [refreshMs]);
+
+ return {format(now)};
+}
diff --git a/src/components/chat/ChatHeader.tsx b/src/components/chat/ChatHeader.tsx
new file mode 100644
index 00000000..ee6d3b6b
--- /dev/null
+++ b/src/components/chat/ChatHeader.tsx
@@ -0,0 +1,33 @@
+// src/components/chat/ChatHeader.tsx
+import { Icon } from '@/components/Icon';
+
+type Props = {
+ title: string;
+ memberCount?: number;
+ onBack: () => void;
+};
+
+export default function ChatHeader({ title, memberCount = 65, onBack }: Props) {
+ return (
+
+ );
+}
diff --git a/src/components/chat/ChatInput.tsx b/src/components/chat/ChatInput.tsx
new file mode 100644
index 00000000..1af20bac
--- /dev/null
+++ b/src/components/chat/ChatInput.tsx
@@ -0,0 +1,174 @@
+// src/components/chat/ChatInput.tsx
+import { useEffect, useRef, useState, useCallback } from 'react';
+import { Icon } from '@/components/Icon';
+
+type Props = {
+ onSend: (text: string) => Promise | void;
+};
+
+export default function ChatInput({ onSend }: Props) {
+ const [value, setValue] = useState('');
+ const taRef = useRef(null);
+ const fileInputRef = useRef(null); // 파일 input ref 추가
+
+ // 스펙
+ const BAR_MIN = 56;
+ const W_EMPTY = 167;
+ const W_TYPED = 239;
+ const ONE_H = 40;
+ const TWO_H = 60;
+ const BAR_PADY = 8;
+ const ONE_PADY = 4;
+ const TWO_PADY = 8;
+
+ const [boxH, setBoxH] = useState(ONE_H);
+ const hasText = value.trim().length > 0;
+
+ const autoResize = () => {
+ const el = taRef.current;
+ if (!el) return;
+
+ el.style.width = `${hasText ? W_TYPED : W_EMPTY}px`;
+ el.style.height = 'auto';
+
+ const cs = window.getComputedStyle(el);
+ const lineH = parseFloat(cs.lineHeight) || 20;
+ const padV = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
+ const borderV = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);
+ const EPS = 6;
+ const oneLineThreshold = lineH + padV + borderV + EPS;
+
+ const contentH = el.scrollHeight;
+ const target = contentH > oneLineThreshold ? TWO_H : ONE_H;
+ setBoxH(target);
+
+ const maxTextAreaHeight = target === TWO_H ? 44 : 32;
+ el.style.height = `${Math.min(contentH, maxTextAreaHeight)}px`;
+ el.style.maxHeight = `${maxTextAreaHeight}px`;
+ el.style.overflowY = contentH > maxTextAreaHeight ? 'auto' : 'hidden';
+ };
+
+ useEffect(() => {
+ autoResize();
+ }, [value]);
+
+ const send = useCallback(async () => {
+ const text = value.trim();
+ if (!text) return;
+ await onSend(text);
+ setValue('');
+ taRef.current?.focus();
+ }, [onSend, value]);
+
+ const onKeyDown: React.KeyboardEventHandler = (e) => {
+ if (e.nativeEvent.isComposing) return;
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ void send();
+ }
+ };
+
+ // 파일 선택 핸들러
+ const handleImageClick = () => {
+ fileInputRef.current?.click();
+ };
+
+ const handleFileSelect = (e: React.ChangeEvent) => {
+ const files = e.target.files;
+ if (files && files.length > 0) {
+ console.log('선택된 파일:', files[0]);
+ // 파일 업로드 로직 추가 가능
+ }
+ };
+
+ const currentPadY = boxH === TWO_H ? TWO_PADY : ONE_PADY;
+
+ return (
+
+ {/* 좌측 아이콘들 */}
+ {!hasText ? (
+ <>
+
+
+
+
+ >
+ ) : (
+
+ )}
+
+ {/* 입력칸 - 중앙 정렬 및 border-radius 조건부 */}
+
+
+ {/* 우측 아이콘 */}
+ {hasText ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/components/chat/ChatScreen.tsx b/src/components/chat/ChatScreen.tsx
new file mode 100644
index 00000000..d7e82c8d
--- /dev/null
+++ b/src/components/chat/ChatScreen.tsx
@@ -0,0 +1,23 @@
+// src/components/chat/ChatScreen.tsx
+import MessageList from './MessageList';
+import type { TextMessage, User } from '@/types/chat';
+
+type Props = {
+ messages: TextMessage[];
+ usersById: Record;
+ meId: string;
+ onToggleReaction: (messageId: string) => void; // 추가
+};
+
+export default function ChatScreen({ messages, usersById, meId, onToggleReaction }: Props) {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/chat/DateDivider.tsx b/src/components/chat/DateDivider.tsx
new file mode 100644
index 00000000..bbd384ac
--- /dev/null
+++ b/src/components/chat/DateDivider.tsx
@@ -0,0 +1,11 @@
+// src/components/chat/DateDivider.tsx
+export default function DateDivider({ label = '2025년 9월 18일 목요일' }: { label?: string }) {
+ return (
+
+ {/* 144x21, radius 32, padding: T/B 2px, L/R 12px, bg gray500, text white(Caption_M_12) */}
+
+ {label}
+
+
+ );
+}
diff --git a/src/components/chat/MessageBubble.tsx b/src/components/chat/MessageBubble.tsx
new file mode 100644
index 00000000..893c68e6
--- /dev/null
+++ b/src/components/chat/MessageBubble.tsx
@@ -0,0 +1,119 @@
+// src/components/chat/MessageBubble.tsx
+import type { TextMessage, User } from '@/types/chat';
+import { Icon } from '@/components/Icon';
+
+type Props = {
+ message: TextMessage;
+ isMine: boolean;
+ user?: User;
+ showAvatar?: boolean;
+ time?: string;
+ spacing?: 'same-user' | 'different-user' | 'first';
+ onToggleReaction: (messageId: string) => void;
+};
+
+export default function MessageBubble({
+ message,
+ isMine,
+ user,
+ showAvatar,
+ time,
+ spacing = 'same-user',
+ onToggleReaction,
+}: Props) {
+ const Time = ({ side }: { side: 'left' | 'right' }) =>
+ time ? (
+
+ {time}
+
+ ) : null;
+
+ const marginTop = spacing === 'first' ? '' : spacing === 'different-user' ? 'mt-4' : 'mt-1';
+
+ const handleDoubleClick = () => {
+ onToggleReaction(message.id);
+ };
+
+ // 하트 클릭 핸들러 추가
+ const handleHeartClick = (e: React.MouseEvent) => {
+ e.stopPropagation(); // 이벤트 버블링 방지
+ onToggleReaction(message.id);
+ };
+
+ return (
+
+ {!isMine && (
+
+ {showAvatar ? (
+ user?.avatarUrl ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ )}
+
+ )}
+
+ {isMine &&
}
+
+
+ {!isMine && showAvatar && (
+
{user?.name ?? ''}
+ )}
+
+
+ {/* 말풍선 */}
+
+
+ {/* 하트 이모지 - 클릭 시 취소 */}
+ {message.reaction && (
+
+ ❤️
+
+ )}
+
+
+
+ {!isMine &&
}
+
+ );
+}
diff --git a/src/components/chat/MessageItem.tsx b/src/components/chat/MessageItem.tsx
new file mode 100644
index 00000000..8eddf7d1
--- /dev/null
+++ b/src/components/chat/MessageItem.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import type { TextMessage, User } from '@/types/chat';
+
+type Props = {
+ message: TextMessage;
+ user: User;
+ isMe: boolean;
+};
+
+export default React.memo(function MessageItem({ message, user, isMe }: Props) {
+ return (
+
+ {/* 상대 메시지일 때만 아바타 표시 */}
+ {!isMe && (
+
+ )}
+
+ {/* 본문 영역 */}
+
+ {/* 상대 이름 (내 메시지엔 없음) */}
+ {!isMe &&
{user.name}
}
+
+ {/* 말풍선 */}
+
+ {message.kind === 'text' && (
+
{message.text}
+ )}
+
+
+ {/* 시간: 말풍선 아래 오른쪽 정렬 */}
+
+ {new Date(message.createdAt).toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit',
+ })}
+
+
+
+ );
+});
diff --git a/src/components/chat/MessageList.tsx b/src/components/chat/MessageList.tsx
new file mode 100644
index 00000000..11c5e66a
--- /dev/null
+++ b/src/components/chat/MessageList.tsx
@@ -0,0 +1,112 @@
+// src/components/chat/MessageList.tsx
+import { useEffect, useMemo, useRef } from 'react';
+import type { TextMessage, User } from '@/types/chat';
+import MessageBubble from './MessageBubble';
+
+type Props = {
+ messages: TextMessage[];
+ usersById: Record;
+ meId: string;
+ onToggleReaction: (messageId: string) => void;
+};
+
+function ymd(iso: string) {
+ return iso.slice(0, 10);
+}
+
+function hm(iso: string) {
+ const d = new Date(iso);
+ const hh = String(d.getHours()).padStart(2, '0');
+ const mm = String(d.getMinutes()).padStart(2, '0');
+ return `${hh}:${mm}`;
+}
+
+function minuteKey(iso: string) {
+ const d = new Date(iso);
+ return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}-${d.getHours()}:${d.getMinutes()}`;
+}
+
+export default function MessageList({ messages, usersById, meId, onToggleReaction }: Props) {
+ const endRef = useRef(null);
+
+ const sections = useMemo(() => {
+ const byDay = new Map();
+
+ for (const m of messages) {
+ const k = ymd(m.createdAt);
+ if (!byDay.has(k)) byDay.set(k, []);
+ byDay.get(k)!.push(m);
+ }
+
+ for (const [, list] of byDay) {
+ list.sort((a, b) => +new Date(a.createdAt) - +new Date(b.createdAt));
+ }
+
+ return Array.from(byDay.entries()).sort(([a], [b]) => (a < b ? -1 : 1));
+ }, [messages]);
+
+ useEffect(() => {
+ endRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
+ }, [messages.length]);
+
+ return (
+
+ {sections.map(([day, list], sectionIdx) => {
+ const isFirstSection = sectionIdx === 0;
+
+ return (
+
+
+
+ {new Date(day).toLocaleDateString('ko-KR', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ weekday: 'long',
+ })}
+
+
+
+ {list.map((m, idx) => {
+ if (!m.text) return null;
+
+ const isMine = m.userId === meId;
+ const prev = list[idx - 1];
+ const next = list[idx + 1];
+
+ const showAvatar = !isMine && (!prev || prev.userId !== m.userId);
+ const showTime = !next || minuteKey(next.createdAt) !== minuteKey(m.createdAt);
+ const user = usersById[m.userId];
+
+ // 간격 계산: 이전 메시지에 하트가 있으면 first 처럼 취급
+ let spacing: 'first' | 'different-user' | 'same-user';
+ if (!prev || !prev.text) {
+ spacing = 'first';
+ } else if (prev.userId !== m.userId) {
+ // 다른 사용자의 메시지
+ spacing = 'different-user';
+ } else {
+ // 같은 사용자의 메시지
+ spacing = 'same-user';
+ }
+
+ return (
+
+ );
+ })}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/src/components/chatrooms/BusinessChatList.tsx b/src/components/chatrooms/BusinessChatList.tsx
new file mode 100644
index 00000000..076e61ff
--- /dev/null
+++ b/src/components/chatrooms/BusinessChatList.tsx
@@ -0,0 +1,36 @@
+// src/components/chatrooms/BusinessChatList.tsx
+import { useChatList } from '@/features/chat/hooks/useChatList';
+import ChatRoomItem from './ChatRoomItem';
+import { useEffect, useState } from 'react';
+
+export default function BusinessChatList() {
+ const allChatRooms = useChatList();
+ const [, forceUpdate] = useState(0);
+
+ // (디버깅용) 주기적 리렌더 유지: 필요 없으면 삭제해도 됨
+ useEffect(() => {
+ const interval = setInterval(() => {
+ forceUpdate((prev) => prev + 1);
+ }, 1000);
+ return () => clearInterval(interval);
+ }, []);
+
+ const businessRooms = allChatRooms.filter((room) => room.type === 'business');
+
+ return (
+
+ {businessRooms.map((room) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/chatrooms/ChatRoomItem.tsx b/src/components/chatrooms/ChatRoomItem.tsx
new file mode 100644
index 00000000..11baf81f
--- /dev/null
+++ b/src/components/chatrooms/ChatRoomItem.tsx
@@ -0,0 +1,83 @@
+// src/components/chatrooms/ChatRoomItem.tsx
+import { Icon } from '@/components/Icon';
+import { useNavigate } from 'react-router-dom';
+
+type Props = {
+ profileImage?: string;
+ roomName: string;
+ memberCount?: number;
+ lastMessage: string;
+ time: string;
+ unreadCount?: number;
+ chatId?: string;
+};
+
+export default function ChatRoomItem({
+ profileImage = 'default-profile',
+ roomName,
+ memberCount,
+ lastMessage,
+ time,
+ unreadCount = 0,
+ chatId,
+}: Props) {
+ const navigate = useNavigate();
+
+ const messageColor = unreadCount > 0 ? 'var(--gray-800)' : 'var(--gray-500)';
+
+ const handleClick = () => {
+ if (chatId) {
+ navigate(`/chat/${chatId}`);
+ }
+ };
+
+ const isClickable = !!chatId;
+
+ return (
+
+
+
+
+
+
+
+
+ {roomName}
+ {memberCount !== undefined && (
+ {memberCount}
+ )}
+
+
{time}
+
+
+
+ {/* ▼ 미리보기(최근 메시지) 영역: 가로 200px / 세로 40px, 최대 2줄 */}
+
+ {lastMessage}
+
+
+ {unreadCount > 0 && (
+
+ {unreadCount}
+
+ )}
+
+
+
+ );
+}
diff --git a/src/components/chatrooms/FriendChatList.tsx b/src/components/chatrooms/FriendChatList.tsx
new file mode 100644
index 00000000..46769a1e
--- /dev/null
+++ b/src/components/chatrooms/FriendChatList.tsx
@@ -0,0 +1,94 @@
+// src/components/chatrooms/FriendChatList.tsx
+import ChatRoomItem from './ChatRoomItem';
+
+export default function FriendChatList() {
+ const chatRooms = [
+ {
+ id: 1,
+ chatId: undefined,
+ profileImage: 'friend-chat-1',
+ roomName: '세오스',
+ memberCount: undefined,
+ lastMessage: '오늘 우리 어디서 만나?',
+ time: '오전 10:30',
+ unreadCount: 4,
+ },
+ {
+ id: 2,
+ chatId: undefined,
+ profileImage: 'friend-chat-2',
+ roomName: '세오스 생일파티',
+ memberCount: 8,
+ lastMessage: '케이크는 미리 예약했고 파티룸도 예\n약 완료입니당~!',
+ time: '오후 6:30',
+ unreadCount: 0,
+ },
+ {
+ id: 3,
+ chatId: undefined,
+ profileImage: 'friend-chat-3',
+ roomName: '한강가자',
+ memberCount: 12,
+ lastMessage: '26일 저녁 다들 어때?',
+ time: '9월 17일',
+ unreadCount: 356,
+ },
+ {
+ id: 4,
+ chatId: undefined,
+ profileImage: 'friend-chat-4',
+ roomName: '오스세',
+ memberCount: undefined,
+ lastMessage: '지금 모햅',
+ time: '25.07.16',
+ unreadCount: 0,
+ },
+ {
+ id: 5,
+ chatId: undefined,
+ profileImage: 'friend-chat-4',
+ roomName: '오스세',
+ memberCount: undefined,
+ lastMessage: '허거덩~!',
+ time: '25.04.08',
+ unreadCount: 2,
+ },
+ {
+ id: 6,
+ chatId: undefined,
+ profileImage: 'friend-chat-4',
+ roomName: '오스세',
+ memberCount: undefined,
+ lastMessage: '수강신청 어떻게 됐어?',
+ time: '24.12.30',
+ unreadCount: 0,
+ },
+ {
+ id: 7,
+ chatId: undefined,
+ profileImage: 'friend-chat-4',
+ roomName: '오스세',
+ memberCount: undefined,
+ lastMessage: '진짜???',
+ time: '24.09.28',
+ unreadCount: 0,
+ },
+ ];
+
+ return (
+
+ {chatRooms.map((room) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/chatrooms/Header.tsx b/src/components/chatrooms/Header.tsx
new file mode 100644
index 00000000..c0cb04fc
--- /dev/null
+++ b/src/components/chatrooms/Header.tsx
@@ -0,0 +1,49 @@
+// src/components/chatrooms/Header.tsx
+import { Icon } from '@/components/Icon';
+
+type Props = {
+ activeTab: 'friend' | 'business';
+ setActiveTab: (tab: 'friend' | 'business') => void;
+};
+
+export default function Header({ activeTab, setActiveTab }: Props) {
+ return (
+
+ {/* 왼쪽: 대화 텍스트 */}
+
대화
+
+ {/* 오른쪽: 아이콘들 + 탭 */}
+
+
+
+
+ {/* 탭 컨테이너 */}
+
+ {/* 친구 탭 */}
+
+
+ {/* 비즈니스 탭 */}
+
+
+
+
+ );
+}
diff --git a/src/components/chatrooms/Menu.tsx b/src/components/chatrooms/Menu.tsx
new file mode 100644
index 00000000..a607e1f4
--- /dev/null
+++ b/src/components/chatrooms/Menu.tsx
@@ -0,0 +1,91 @@
+// src/components/chatrooms/Menu.tsx
+import { useNavigate, useLocation } from 'react-router-dom';
+import { Icon } from '@/components/Icon';
+
+type MenuType = 'home' | 'chat' | 'call' | 'more';
+
+export default function Menu() {
+ const navigate = useNavigate();
+ const location = useLocation();
+
+ const getActiveMenu = (): MenuType => {
+ if (location.pathname === '/') return 'home';
+ if (location.pathname.startsWith('/chats')) return 'chat';
+ if (location.pathname.startsWith('/call')) return 'call';
+ if (location.pathname.startsWith('/more')) return 'more';
+ return 'chat';
+ };
+
+ const activeMenu = getActiveMenu();
+
+ const menus = [
+ {
+ id: 'home' as MenuType,
+ label: '홈',
+ icon: 'home',
+ iconActive: 'home-dark',
+ path: '/',
+ },
+ {
+ id: 'chat' as MenuType,
+ label: '대화',
+ icon: 'chat',
+ iconActive: 'chat-dark',
+ path: '/chats',
+ },
+ {
+ id: 'call' as MenuType,
+ label: '전화',
+ icon: 'call',
+ iconActive: 'call',
+ path: '/call',
+ },
+ {
+ id: 'more' as MenuType,
+ label: '더보기',
+ icon: 'vector',
+ iconActive: 'vector',
+ path: '/more',
+ },
+ ];
+
+ return (
+
+ {menus.map((menu) => {
+ const isActive = activeMenu === menu.id;
+
+ return (
+
+ );
+ })}
+
+ );
+}
diff --git a/src/components/chatrooms/Roomsearch.tsx b/src/components/chatrooms/Roomsearch.tsx
new file mode 100644
index 00000000..9d2bfb40
--- /dev/null
+++ b/src/components/chatrooms/Roomsearch.tsx
@@ -0,0 +1,20 @@
+// src/components/chatrooms/Roomsearch.tsx
+import { Icon } from '@/components/Icon';
+
+export default function Roomsearch() {
+ return (
+
+ {/* pb-6 = 24px (하단 간격) 추가, bg-white 추가 */}
+
+ {/* 왼쪽: 검색 아이콘 + 텍스트 */}
+
+
+ 톡방 검색
+
+
+ {/* 오른쪽: 스캔 아이콘 */}
+
+
+
+ );
+}
diff --git a/src/components/common/StatusBar.tsx b/src/components/common/StatusBar.tsx
new file mode 100644
index 00000000..08c37f8b
--- /dev/null
+++ b/src/components/common/StatusBar.tsx
@@ -0,0 +1,40 @@
+// src/components/common/StatusBar.tsx
+import { useEffect, useState } from 'react';
+import { Icon } from '@/components/Icon';
+
+const pad2 = (n: number) => (n < 10 ? `0${n}` : String(n));
+
+export default function StatusBar() {
+ const [now, setNow] = useState(() => new Date());
+ useEffect(() => {
+ const t = setInterval(() => setNow(new Date()), 30_000);
+ return () => clearInterval(t);
+ }, []);
+ const hh = pad2(now.getHours());
+ const mm = pad2(now.getMinutes());
+
+ return (
+ // ✅ 정확히 375×47, 여백 없음
+
+
+ {/* 시계: 88×47 박스 우측 정렬 */}
+
+
+ {hh}:{mm}
+
+
+
+
+
+ {/* 우측 인디케이터: 오른쪽/위/아래 6.5px 바깥 여백 */}
+
+
+
+
+
+ );
+}
diff --git a/src/components/dev/ErrorBoundary.tsx b/src/components/dev/ErrorBoundary.tsx
new file mode 100644
index 00000000..9fa63e6a
--- /dev/null
+++ b/src/components/dev/ErrorBoundary.tsx
@@ -0,0 +1,23 @@
+// src/components/dev/ErrorBoundary.tsx
+import React from 'react';
+
+type State = { error?: Error };
+export default class ErrorBoundary extends React.Component {
+ state: State = {};
+ static getDerivedStateFromError(error: Error) {
+ return { error };
+ }
+ componentDidCatch(error: Error, info: React.ErrorInfo) {
+ console.error('[ErrorBoundary]', error, info);
+ }
+ render() {
+ if (this.state.error) {
+ return (
+
+ ⚠️ 런타임 에러 발생: {this.state.error.message}
+
+ );
+ }
+ return this.props.children;
+ }
+}
diff --git a/src/components/dev/RouteProbe.tsx b/src/components/dev/RouteProbe.tsx
new file mode 100644
index 00000000..53d5f29e
--- /dev/null
+++ b/src/components/dev/RouteProbe.tsx
@@ -0,0 +1,25 @@
+// src/components/dev/RouteProbe.tsx
+import { useParams, useLocation } from 'react-router-dom';
+
+export default function RouteProbe() {
+ const p = useParams();
+ const loc = useLocation();
+ return (
+
+ {loc.pathname} | chatId: {p.chatId ?? '(none)'}
+
+ );
+}
diff --git a/src/components/home/FriendItem.tsx b/src/components/home/FriendItem.tsx
new file mode 100644
index 00000000..821ed328
--- /dev/null
+++ b/src/components/home/FriendItem.tsx
@@ -0,0 +1,65 @@
+// src/components/home/FriendItem.tsx
+import { Icon } from '@/components/Icon';
+import { useNavigate } from 'react-router-dom';
+
+type Props = {
+ profileImage: string;
+ name: string;
+ count?: number;
+};
+
+export default function FriendItem({ profileImage, name, count }: Props) {
+ const navigate = useNavigate();
+
+ const handleClick = () => {
+ // '세오스'이면 친구 프로필로 이동 (모두 같은 페이지)
+ if (name === '세오스') {
+ navigate('/profile/ceos');
+ }
+ };
+
+ return (
+
+ {/* 프로필 사진 */}
+
+
+
+
+ {/* 이름 */}
+
+ {name}
+
+
+ {/* 카운트 + 화살표 (공식 계정, 추천 친구만) */}
+ {count !== undefined && (
+
+ )}
+
+ );
+}
diff --git a/src/components/home/FriendList.tsx b/src/components/home/FriendList.tsx
new file mode 100644
index 00000000..f385227f
--- /dev/null
+++ b/src/components/home/FriendList.tsx
@@ -0,0 +1,88 @@
+// src/components/home/FriendList.tsx
+import { useState } from 'react';
+import { Icon } from '@/components/Icon';
+import FriendItem from './FriendItem';
+
+type Friend = {
+ id: string;
+ profileImage: string;
+ name: string;
+ count?: number;
+};
+
+export default function FriendList() {
+ const [businessOpen, setBusinessOpen] = useState(false);
+ const [friendOpen, setFriendOpen] = useState(false);
+
+ // 비즈니스 친구 4명
+ const businessFriends: Friend[] = Array.from({ length: 4 }, (_, i) => ({
+ id: `business-${i}`,
+ profileImage: 'friend-chat-4',
+ name: '세오스',
+ }));
+
+ // 친구 65명
+ const friends: Friend[] = [
+ { id: 'official', profileImage: 'line-img', name: '공식 계정', count: 10 },
+ { id: 'recommended', profileImage: 'friend-chat-4', name: '추천 친구', count: 136 },
+ ...Array.from({ length: 63 }, (_, i) => ({
+ id: `friend-${i}`,
+ profileImage: 'friend-chat-4',
+ name: '세오스',
+ })),
+ ];
+
+ return (
+
+ {/* 비즈니스 탭 */}
+
+
+ {/* 비즈니스 친구 목록 */}
+ {businessOpen && (
+
+ {businessFriends.map((friend) => (
+
+ ))}
+
+ )}
+
+ {/* 친구 탭 */}
+
+
+ {/* 친구 목록 */}
+ {friendOpen && (
+
+ {friends.map((friend) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/components/home/FriendSearch.tsx b/src/components/home/FriendSearch.tsx
new file mode 100644
index 00000000..1720e94a
--- /dev/null
+++ b/src/components/home/FriendSearch.tsx
@@ -0,0 +1,23 @@
+// src/components/home/Friendsearch.tsx
+import { Icon } from '@/components/Icon';
+
+export default function Friendsearch() {
+ return (
+
+
+ {/* 왼쪽: 검색 아이콘 + 텍스트 */}
+
+
+ {/* 오른쪽: 스캔 아이콘 */}
+
+
+
+
+
+ );
+}
diff --git a/src/components/home/Header.tsx b/src/components/home/Header.tsx
new file mode 100644
index 00000000..d05eb436
--- /dev/null
+++ b/src/components/home/Header.tsx
@@ -0,0 +1,17 @@
+// src/components/home/Header.tsx
+import { Icon } from '@/components/Icon';
+
+export default function Header() {
+ return (
+
+ {/* 오른쪽 아이콘들 - 역순으로 배치 */}
+
+ {/* gap-4 = 16px */}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/home/MiniProfile.tsx b/src/components/home/MiniProfile.tsx
new file mode 100644
index 00000000..1879a204
--- /dev/null
+++ b/src/components/home/MiniProfile.tsx
@@ -0,0 +1,48 @@
+// src/components/home/MiniProfile.tsx
+import { Icon } from '@/components/Icon';
+import { useNavigate } from 'react-router-dom';
+
+export default function MiniProfile() {
+ const navigate = useNavigate();
+
+ return (
+
+ {/* 왼쪽: 프로필 + 이름 (88x38) */}
+
navigate('/profile/me')}
+ >
+ {/* 프로필 이미지 38x38 */}
+
+
+
+
+ {/* 이름 */}
+
+ 세오스
+
+
+
+ {/* 오른쪽: 아이콘들 */}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/home/MyProfile.tsx b/src/components/home/MyProfile.tsx
new file mode 100644
index 00000000..e4d0a91c
--- /dev/null
+++ b/src/components/home/MyProfile.tsx
@@ -0,0 +1,25 @@
+// src/components/home/MyProfile.tsx
+import { useNavigate } from 'react-router-dom';
+import { Icon } from '@/components/Icon';
+
+export default function MyProfile() {
+ const navigate = useNavigate();
+
+ return (
+ navigate('/profile/me')}
+ >
+ {/* 왼쪽: 텍스트 영역 */}
+
+
+ {/* 오른쪽: 프로필 이미지 - Icon 컴포넌트 사용 */}
+
+
+
+
+ );
+}
diff --git a/src/context/ChatContext.tsx b/src/context/ChatContext.tsx
new file mode 100644
index 00000000..e85d4bb6
--- /dev/null
+++ b/src/context/ChatContext.tsx
@@ -0,0 +1,126 @@
+// src/context/ChatContext.tsx
+import React, { createContext, useContext, useMemo, useState } from 'react';
+import type { Id, User, TextMessage, Conversation } from '@/types/chat';
+
+type State = {
+ users: Record;
+ conversations: Record;
+ messagesByChat: Record;
+ meId: Id | null;
+ sendText: (chatId: Id, text: string) => Promise;
+};
+
+const ChatContext = createContext(null);
+
+const CHAT_ID: Id = 'ceos-22';
+
+// 샘플 유저
+const initialUsers: Record = {
+ me: { id: 'me', name: '나', avatarUrl: '/src/assets/react.svg' },
+ o: { id: 'o', name: '오스세', avatarUrl: '/src/assets/react.svg' },
+ s1: { id: 's1', name: '스세오', avatarUrl: '/src/assets/react.svg' },
+ s2: { id: 's2', name: '세세오스', avatarUrl: '/src/assets/react.svg' },
+ s3: { id: 's3', name: '세오오스', avatarUrl: '/src/assets/react.svg' },
+};
+
+// 대화방 (⛳️ Profile.tsx에서 쓰던 participantIds 포함)
+const initialConversations: Record = {
+ [CHAT_ID]: {
+ id: CHAT_ID,
+ title: 'CEOS 22기 잡담방',
+ memberCount: 65,
+ participantIds: ['me', 'o', 's1', 's2', 's3'],
+ },
+};
+
+const iso = (h: number, m: number) => {
+ const d = new Date();
+ d.setHours(h, m, 0, 0);
+ return d.toISOString();
+};
+
+const initialMessages: TextMessage[] = [
+ {
+ id: 'm1',
+ chatId: CHAT_ID,
+ userId: 'o',
+ kind: 'text',
+ text:
+ `[CEOS 22기 잡담방]\n` +
+ `22기 여러분 모두 환영합니다!\n🎉 모두들 서로 반갑게 인사해 주세요!\n\n` +
+ `그리고 여러분 공지 확인 부탁드립니다😄`,
+ createdAt: iso(12, 30),
+ },
+ {
+ id: 'm2',
+ chatId: CHAT_ID,
+ userId: 's1',
+ kind: 'text',
+ text: '22기 여러분 세오스의 가족이 되신걸 환영합니다!!',
+ createdAt: iso(12, 35),
+ },
+ { id: 'm3', chatId: CHAT_ID, userId: 'me', kind: 'text', text: '다들 반갑습니다!!', createdAt: iso(12, 40) },
+ {
+ id: 'm4',
+ chatId: CHAT_ID,
+ userId: 'me',
+ kind: 'text',
+ text: '22기 디자인 세오스입니다! 잘 부탁드립니다!!',
+ createdAt: iso(12, 45),
+ },
+ {
+ id: 'm5',
+ chatId: CHAT_ID,
+ userId: 's2',
+ kind: 'text',
+ text: '안녕하세요! 22기 디자인 세세오스입니다. 잘 부탁드립니다!!',
+ createdAt: iso(12, 50),
+ },
+ {
+ id: 'm6',
+ chatId: CHAT_ID,
+ userId: 's3',
+ kind: 'text',
+ text: '안녕하세요! 22기 디자인 세오오스입니다. 잘 부탁드립니다!!',
+ createdAt: iso(12, 55),
+ },
+];
+
+export function ChatProvider({ children }: { children: React.ReactNode }) {
+ const [users] = useState(initialUsers);
+ const [conversations] = useState(initialConversations);
+ const [messagesByChat, setMessagesByChat] = useState>({
+ [CHAT_ID]: initialMessages,
+ });
+ const [meId] = useState('me');
+
+ const sendText = async (chatId: Id, text: string) => {
+ const t = text.trim();
+ if (!t) return;
+ const msg: TextMessage = {
+ id: crypto.randomUUID(),
+ chatId,
+ userId: meId,
+ kind: 'text',
+ text: t,
+ createdAt: new Date().toISOString(),
+ };
+ setMessagesByChat((prev) => ({
+ ...prev,
+ [chatId]: [...(prev[chatId] ?? []), msg],
+ }));
+ };
+
+ const value = useMemo(
+ () => ({ users, conversations, messagesByChat, meId, sendText }),
+ [users, conversations, messagesByChat, meId],
+ );
+
+ return {children};
+}
+
+export function useChatState() {
+ const ctx = useContext(ChatContext);
+ if (!ctx) throw new Error('useChatState must be used within ChatProvider');
+ return ctx;
+}
diff --git a/src/data/chatList.json b/src/data/chatList.json
new file mode 100644
index 00000000..4fbace99
--- /dev/null
+++ b/src/data/chatList.json
@@ -0,0 +1,112 @@
+[
+ {
+ "id": 1,
+ "chatId": "c_ceos_notice",
+ "profileImage": "ceos-notice",
+ "roomName": "CEOS 22기 공지방",
+ "memberCount": 65,
+ "lastMessage": "[2주차 세션 공지]\n...",
+ "time": "오후 12:30",
+ "unreadCount": 12,
+ "type": "business"
+ },
+ {
+ "id": 2,
+ "chatId": "c_ceos",
+ "profileImage": "ceos-chat",
+ "roomName": "CEOS 22기 잡담방",
+ "memberCount": 65,
+ "lastMessage": "안녕하세요 다들 만나서 반갑습니다!!\n다들 자기소개 부탁드릴게요~! 지...",
+ "time": "오전 10:35",
+ "unreadCount": 0,
+ "type": "business"
+ },
+ {
+ "id": 3,
+ "chatId": "c_ceos_design",
+ "profileImage": "ceos-design",
+ "roomName": "CEOS 22기 디자인파트",
+ "memberCount": 12,
+ "lastMessage": "세미나 자료 공유드립니다.",
+ "time": "9월 14일",
+ "unreadCount": 0,
+ "type": "business"
+ },
+ {
+ "id": 4,
+ "chatId": "c_friend_1",
+ "profileImage": "friend-chat-1",
+ "roomName": "세오스",
+ "memberCount": null,
+ "lastMessage": "오늘 우리 어디서 만나?",
+ "time": "오전 10:30",
+ "unreadCount": 4,
+ "type": "friend"
+ },
+ {
+ "id": 5,
+ "chatId": "c_friend_2",
+ "profileImage": "friend-chat-2",
+ "roomName": "세오스 생일파티",
+ "memberCount": 8,
+ "lastMessage": "케이크는 미리 예약했고 파티룸도 예\n약 완료입니당~!",
+ "time": "오후 6:30",
+ "unreadCount": 0,
+ "type": "friend"
+ },
+ {
+ "id": 6,
+ "chatId": "c_friend_3",
+ "profileImage": "friend-chat-3",
+ "roomName": "한강가자",
+ "memberCount": 12,
+ "lastMessage": "26일 저녁 다들 어때?",
+ "time": "9월 17일",
+ "unreadCount": 356,
+ "type": "friend"
+ },
+ {
+ "id": 7,
+ "chatId": "c_friend_4",
+ "profileImage": "friend-chat-4",
+ "roomName": "오스세",
+ "memberCount": null,
+ "lastMessage": "지금 모햅",
+ "time": "25.07.16",
+ "unreadCount": 0,
+ "type": "friend"
+ },
+ {
+ "id": 8,
+ "chatId": "c_friend_5",
+ "profileImage": "friend-chat-4",
+ "roomName": "오스세",
+ "memberCount": null,
+ "lastMessage": "허거덩~!",
+ "time": "25.04.08",
+ "unreadCount": 2,
+ "type": "friend"
+ },
+ {
+ "id": 9,
+ "chatId": "c_friend_6",
+ "profileImage": "friend-chat-4",
+ "roomName": "오스세",
+ "memberCount": null,
+ "lastMessage": "수강신청 어떻게 됐어?",
+ "time": "24.12.30",
+ "unreadCount": 0,
+ "type": "friend"
+ },
+ {
+ "id": 10,
+ "chatId": "c_friend_7",
+ "profileImage": "friend-chat-4",
+ "roomName": "오스세",
+ "memberCount": null,
+ "lastMessage": "진짜???",
+ "time": "24.09.28",
+ "unreadCount": 0,
+ "type": "friend"
+ }
+]
diff --git a/src/data/messages.json b/src/data/messages.json
new file mode 100644
index 00000000..b5067ef0
--- /dev/null
+++ b/src/data/messages.json
@@ -0,0 +1,50 @@
+[
+ {
+ "id": "m1",
+ "kind": "text",
+ "chatId": "c_ceos",
+ "userId": "u_cony",
+ "text": "[CEOS 22기 잡담방]\n22기 여러분 모두 환영합니다!\n🎉 모두들 서로 반갑게 인사해 주세요!",
+ "createdAt": "2025-09-18T12:30:00+09:00"
+ },
+ {
+ "id": "m1_2",
+ "kind": "text",
+ "chatId": "c_ceos",
+ "userId": "u_cony",
+ "text": "그리고 여러분 공지 확인 부탁드립니다🥳",
+ "createdAt": "2025-09-18T12:30:05+09:00"
+ },
+ {
+ "id": "m2",
+ "kind": "text",
+ "chatId": "c_ceos",
+ "userId": "u_brown",
+ "text": "22기 여러분 세오스의 가족이 되신걸 환영합니다!!",
+ "createdAt": "2025-09-18T12:35:00+09:00"
+ },
+ {
+ "id": "m3",
+ "kind": "text",
+ "chatId": "c_ceos",
+ "userId": "me",
+ "text": "다들 반갑습니다!!",
+ "createdAt": "2025-09-18T12:40:00+09:00"
+ },
+ {
+ "id": "m4",
+ "kind": "text",
+ "chatId": "c_ceos",
+ "userId": "me",
+ "text": "22기 디자인 세오스입니다!\n잘 부탁드립니다!!",
+ "createdAt": "2025-09-18T12:41:00+09:00"
+ },
+ {
+ "id": "m5",
+ "kind": "text",
+ "chatId": "c_ceos",
+ "userId": "u_cceos",
+ "text": "안녕하세요! 22기 디자인 세세오스입니다. 잘 부탁드립니다!!",
+ "createdAt": "2025-09-18T12:45:00+09:00"
+ }
+]
diff --git a/src/data/users.json b/src/data/users.json
new file mode 100644
index 00000000..519d7035
--- /dev/null
+++ b/src/data/users.json
@@ -0,0 +1,22 @@
+{
+ "me": {
+ "id": "me",
+ "name": "세오스",
+ "avatarUrl": "my-profile-big"
+ },
+ "u_brown": {
+ "id": "u_brown",
+ "name": "스세오",
+ "avatarUrl": "ceoss"
+ },
+ "u_cony": {
+ "id": "u_cony",
+ "name": "오스세",
+ "avatarUrl": "ceoos"
+ },
+ "u_cceos": {
+ "id": "u_cceos",
+ "name": "세세오스",
+ "avatarUrl": "cceos"
+ }
+}
diff --git a/src/features/chat/hooks/useAutoScroll.ts b/src/features/chat/hooks/useAutoScroll.ts
new file mode 100644
index 00000000..327aa046
--- /dev/null
+++ b/src/features/chat/hooks/useAutoScroll.ts
@@ -0,0 +1,8 @@
+import { useEffect } from 'react';
+
+/** 어떤 HTMLElement든 받을 수 있고, 항상 null 가능(ref는 기본이 null) */
+export function useAutoScroll(dep: unknown, ref: React.RefObject) {
+ useEffect(() => {
+ ref.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
+ }, [dep, ref]);
+}
diff --git a/src/features/chat/hooks/useChatList.ts b/src/features/chat/hooks/useChatList.ts
new file mode 100644
index 00000000..36b2a1c4
--- /dev/null
+++ b/src/features/chat/hooks/useChatList.ts
@@ -0,0 +1,124 @@
+// src/features/chat/hooks/useChatList.ts
+import { useState, useEffect, useCallback } from 'react';
+import chatListData from '@/data/chatList.json';
+
+type ChatRoom = {
+ id: number;
+ chatId: string | null;
+ profileImage: string;
+ roomName: string;
+ memberCount: number | null;
+ lastMessage: string;
+ time: string;
+ unreadCount: number;
+ type: 'business' | 'friend';
+};
+
+const STORAGE_KEY = 'chatList';
+
+export function useChatList() {
+ const [chatList, setChatList] = useState([]);
+
+ // 로컬스토리지에서 불러오는 함수
+ const loadChatList = useCallback(() => {
+ const saved = localStorage.getItem(STORAGE_KEY);
+ if (saved) {
+ try {
+ const parsed = JSON.parse(saved) as ChatRoom[];
+ setChatList(parsed);
+ console.log('✅ Chat list loaded:', parsed); // 디버깅
+ return;
+ } catch (error) {
+ console.error('Failed to parse chat list:', error);
+ }
+ }
+ // 저장된 데이터 없으면 초기 데이터 사용
+ setChatList(chatListData as ChatRoom[]);
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(chatListData));
+ }, []);
+
+ // 초기 로드
+ useEffect(() => {
+ loadChatList();
+ }, [loadChatList]);
+
+ // storage 이벤트 리스너
+ useEffect(() => {
+ const handleStorageChange = (e?: StorageEvent) => {
+ console.log('🔄 Storage event triggered:', e?.key); // 디버깅
+ loadChatList();
+ };
+
+ const handleCustomEvent = () => {
+ console.log('🔄 Custom event triggered'); // 디버깅
+ loadChatList();
+ };
+
+ // 다른 탭에서의 변경 감지
+ window.addEventListener('storage', handleStorageChange);
+
+ // 같은 탭에서의 변경 감지 (커스텀 이벤트)
+ window.addEventListener('chatListUpdated', handleCustomEvent);
+
+ return () => {
+ window.removeEventListener('storage', handleStorageChange);
+ window.removeEventListener('chatListUpdated', handleCustomEvent);
+ };
+ }, [loadChatList]);
+
+ return chatList;
+}
+
+// 시간 포맷 함수
+function formatTime(date: Date): string {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ const period = hours >= 12 ? '오후' : '오전';
+ const displayHours = hours > 12 ? hours - 12 : hours === 0 ? 12 : hours;
+ return `${period} ${displayHours}:${minutes.toString().padStart(2, '0')}`;
+}
+
+// 채팅 목록 업데이트 함수
+export function updateChatList(chatId: string, lastMessage: string) {
+ console.log('📝 Updating chat list for:', chatId, lastMessage); // 디버깅
+
+ const saved = localStorage.getItem(STORAGE_KEY);
+ if (!saved) {
+ console.error('❌ No chat list found in storage');
+ return;
+ }
+
+ try {
+ const chatList: ChatRoom[] = JSON.parse(saved);
+ const chatIndex = chatList.findIndex((chat) => chat.chatId === chatId);
+
+ console.log('🔍 Found chat at index:', chatIndex); // 디버깅
+
+ if (chatIndex !== -1) {
+ const now = new Date();
+ const time = formatTime(now);
+
+ // 해당 채팅방 업데이트
+ chatList[chatIndex] = {
+ ...chatList[chatIndex],
+ lastMessage,
+ time,
+ };
+
+ console.log('✅ Updated chat:', chatList[chatIndex]); // 디버깅
+
+ // 로컬스토리지에 저장
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(chatList));
+
+ // 커스텀 이벤트 발생
+ const event = new Event('chatListUpdated');
+ window.dispatchEvent(event);
+
+ console.log('🚀 Event dispatched'); // 디버깅
+ } else {
+ console.error('❌ Chat not found with chatId:', chatId);
+ }
+ } catch (error) {
+ console.error('❌ Failed to update chat list:', error);
+ }
+}
diff --git a/src/features/chat/hooks/useComposing.ts b/src/features/chat/hooks/useComposing.ts
new file mode 100644
index 00000000..458d9652
--- /dev/null
+++ b/src/features/chat/hooks/useComposing.ts
@@ -0,0 +1,7 @@
+import { useCallback, useState } from 'react';
+export function useComposing() {
+ const [isComposing, set] = useState(false);
+ const onCompositionStart = useCallback(() => set(true), []);
+ const onCompositionEnd = useCallback(() => set(false), []);
+ return { isComposing, onCompositionStart, onCompositionEnd };
+}
diff --git a/src/features/chat/hooks/useLocalMessages.ts b/src/features/chat/hooks/useLocalMessages.ts
new file mode 100644
index 00000000..2b1652da
--- /dev/null
+++ b/src/features/chat/hooks/useLocalMessages.ts
@@ -0,0 +1,121 @@
+// src/features/chat/hooks/useLocalMessages.ts
+import { useReducer, useEffect, useCallback } from 'react';
+import { updateChatList } from './useChatList';
+
+export type TextMessage = {
+ id: string;
+ kind: 'text';
+ chatId: string;
+ userId: string;
+ text: string;
+ createdAt: string;
+ reaction?: '❤️' | null; // 추가
+};
+
+type State = {
+ messages: TextMessage[];
+ isLoading: boolean;
+};
+
+type Action =
+ | { type: 'LOAD_MESSAGES'; payload: TextMessage[] }
+ | { type: 'ADD_MESSAGE'; payload: TextMessage }
+ | { type: 'TOGGLE_REACTION'; payload: { messageId: string } } // 추가
+ | { type: 'SET_LOADING'; payload: boolean };
+
+function messagesReducer(state: State, action: Action): State {
+ switch (action.type) {
+ case 'LOAD_MESSAGES':
+ return {
+ ...state,
+ messages: action.payload,
+ isLoading: false,
+ };
+ case 'ADD_MESSAGE':
+ return {
+ ...state,
+ messages: [...state.messages, action.payload],
+ };
+ case 'TOGGLE_REACTION': // 추가
+ return {
+ ...state,
+ messages: state.messages.map((msg) =>
+ msg.id === action.payload.messageId ? { ...msg, reaction: msg.reaction ? null : '❤️' } : msg,
+ ),
+ };
+ case 'SET_LOADING':
+ return {
+ ...state,
+ isLoading: action.payload,
+ };
+ default:
+ return state;
+ }
+}
+
+const initialState: State = {
+ messages: [],
+ isLoading: true,
+};
+
+export function useLocalMessages(chatId: string, meId: string, seed: TextMessage[]) {
+ const [state, dispatch] = useReducer(messagesReducer, initialState);
+
+ useEffect(() => {
+ dispatch({ type: 'SET_LOADING', payload: true });
+
+ const storageKey = `messages:${chatId}`;
+ const saved = localStorage.getItem(storageKey);
+
+ if (saved) {
+ try {
+ const parsed = JSON.parse(saved) as TextMessage[];
+ dispatch({ type: 'LOAD_MESSAGES', payload: parsed });
+ } catch (error) {
+ console.error('Failed to parse messages:', error);
+ dispatch({ type: 'LOAD_MESSAGES', payload: seed });
+ }
+ } else {
+ dispatch({ type: 'LOAD_MESSAGES', payload: seed });
+ localStorage.setItem(storageKey, JSON.stringify(seed));
+ }
+ }, [chatId, seed]);
+
+ useEffect(() => {
+ if (!state.isLoading && state.messages.length > 0) {
+ const storageKey = `messages:${chatId}`;
+ localStorage.setItem(storageKey, JSON.stringify(state.messages));
+ }
+ }, [chatId, state.messages, state.isLoading]);
+
+ const sendText = useCallback(
+ async (text: string) => {
+ if (!text.trim()) return;
+
+ const newMessage: TextMessage = {
+ id: `m_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
+ kind: 'text',
+ chatId,
+ userId: meId,
+ text: text.trim(),
+ createdAt: new Date().toISOString(),
+ };
+
+ dispatch({ type: 'ADD_MESSAGE', payload: newMessage });
+ updateChatList(chatId, text.trim());
+ },
+ [chatId, meId],
+ );
+
+ // 추가: 하트 반응 토글 함수
+ const toggleReaction = useCallback((messageId: string) => {
+ dispatch({ type: 'TOGGLE_REACTION', payload: { messageId } });
+ }, []);
+
+ return {
+ messages: state.messages,
+ isLoading: state.isLoading,
+ sendText,
+ toggleReaction, // 추가
+ };
+}
diff --git a/src/features/chat/hooks/useMessages.ts b/src/features/chat/hooks/useMessages.ts
new file mode 100644
index 00000000..22bf8837
--- /dev/null
+++ b/src/features/chat/hooks/useMessages.ts
@@ -0,0 +1,31 @@
+// src/features/chat/hooks/useMessages.ts
+import { useMemo } from 'react';
+import { useChatState } from '@/context/ChatContext';
+import type { Id, TextMessage, User, Conversation } from '@/types/chat';
+
+/** 채팅방별 메시지/전송 (개별 방) */
+export function useChatRoom(chatId: Id) {
+ const { users, messagesByChat, meId, sendText } = useChatState();
+ const messages: TextMessage[] = useMemo(
+ () => (messagesByChat[chatId] ?? []) as TextMessage[],
+ [messagesByChat, chatId],
+ );
+ const onSend = (text: string) => sendText(chatId, text);
+ return { users: users as Record, messages, meId: meId!, sendText: onSend };
+}
+
+/** 채팅방 목록/정보 (목록 화면) */
+export function useConversations() {
+ const { conversations, messagesByChat } = useChatState();
+ const list: (Conversation & { lastMessageAt?: string; messageCount: number })[] = useMemo(() => {
+ return Object.values(conversations).map((cv) => {
+ const msgs = messagesByChat[cv.id] ?? [];
+ return {
+ ...cv,
+ messageCount: msgs.length,
+ lastMessageAt: msgs.at(-1)?.createdAt,
+ };
+ });
+ }, [conversations, messagesByChat]);
+ return { conversations: list };
+}
diff --git a/src/features/chat/utils/datetime.ts b/src/features/chat/utils/datetime.ts
new file mode 100644
index 00000000..8662b463
--- /dev/null
+++ b/src/features/chat/utils/datetime.ts
@@ -0,0 +1,16 @@
+// src/features/chat/utils/datetime.ts
+const WEEK = ['일', '월', '화', '수', '목', '금', '토'];
+
+export function formatHM(iso: string) {
+ const d = new Date(iso);
+ const hh = String(d.getHours()).padStart(2, '0');
+ const mm = String(d.getMinutes()).padStart(2, '0');
+ return `${hh}:${mm}`;
+}
+
+export function formatKDate(yyyyMmDd: string) {
+ // yyyy-mm-dd
+ const [y, m, d] = yyyyMmDd.split('-').map((v) => parseInt(v, 10));
+ const dt = new Date(y, m - 1, d);
+ return `${y}년 ${m}월 ${d}일 (${WEEK[dt.getDay()]})`;
+}
diff --git a/src/icons/Bookmark.svg b/src/icons/Bookmark.svg
new file mode 100644
index 00000000..42f88b06
--- /dev/null
+++ b/src/icons/Bookmark.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/icons/Burger.svg b/src/icons/Burger.svg
new file mode 100644
index 00000000..a2eb13c1
--- /dev/null
+++ b/src/icons/Burger.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/Call.svg b/src/icons/Call.svg
new file mode 100644
index 00000000..00bd7c66
--- /dev/null
+++ b/src/icons/Call.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/Camera.svg b/src/icons/Camera.svg
new file mode 100644
index 00000000..af50a658
--- /dev/null
+++ b/src/icons/Camera.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/Chat.svg b/src/icons/Chat.svg
new file mode 100644
index 00000000..9bec4164
--- /dev/null
+++ b/src/icons/Chat.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/icons/ChatPlus.svg b/src/icons/ChatPlus.svg
new file mode 100644
index 00000000..712cc2c4
--- /dev/null
+++ b/src/icons/ChatPlus.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/icons/Cross.svg b/src/icons/Cross.svg
new file mode 100644
index 00000000..fb18c106
--- /dev/null
+++ b/src/icons/Cross.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/CrossX.svg b/src/icons/CrossX.svg
new file mode 100644
index 00000000..85317ec6
--- /dev/null
+++ b/src/icons/CrossX.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/Home.svg b/src/icons/Home.svg
new file mode 100644
index 00000000..a0455e02
--- /dev/null
+++ b/src/icons/Home.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/icons/Image.svg b/src/icons/Image.svg
new file mode 100644
index 00000000..49af7883
--- /dev/null
+++ b/src/icons/Image.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/icons/Notification.svg b/src/icons/Notification.svg
new file mode 100644
index 00000000..9546d1f3
--- /dev/null
+++ b/src/icons/Notification.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/Scan.svg b/src/icons/Scan.svg
new file mode 100644
index 00000000..5afff359
--- /dev/null
+++ b/src/icons/Scan.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/icons/Send.svg b/src/icons/Send.svg
new file mode 100644
index 00000000..23782349
--- /dev/null
+++ b/src/icons/Send.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/Setting.svg b/src/icons/Setting.svg
new file mode 100644
index 00000000..e4f8e1f2
--- /dev/null
+++ b/src/icons/Setting.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/Vector.svg b/src/icons/Vector.svg
new file mode 100644
index 00000000..3bfc1444
--- /dev/null
+++ b/src/icons/Vector.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/Voice.svg b/src/icons/Voice.svg
new file mode 100644
index 00000000..9afb755e
--- /dev/null
+++ b/src/icons/Voice.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/icons/add-user.svg b/src/icons/add-user.svg
new file mode 100644
index 00000000..caa37794
--- /dev/null
+++ b/src/icons/add-user.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/arrow-down-2.svg b/src/icons/arrow-down-2.svg
new file mode 100644
index 00000000..4aded8b4
--- /dev/null
+++ b/src/icons/arrow-down-2.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/arrow-right.svg b/src/icons/arrow-right.svg
new file mode 100644
index 00000000..b3577cb9
--- /dev/null
+++ b/src/icons/arrow-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/arrow-up-2.svg b/src/icons/arrow-up-2.svg
new file mode 100644
index 00000000..7b701a7c
--- /dev/null
+++ b/src/icons/arrow-up-2.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/battery.svg b/src/icons/battery.svg
new file mode 100644
index 00000000..a7f8b941
--- /dev/null
+++ b/src/icons/battery.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/bookmark-white.svg b/src/icons/bookmark-white.svg
new file mode 100644
index 00000000..3895524b
--- /dev/null
+++ b/src/icons/bookmark-white.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/icons/call-white.svg b/src/icons/call-white.svg
new file mode 100644
index 00000000..625c82c9
--- /dev/null
+++ b/src/icons/call-white.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/cceos.svg b/src/icons/cceos.svg
new file mode 100644
index 00000000..216ed7a1
--- /dev/null
+++ b/src/icons/cceos.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/ceoos.svg b/src/icons/ceoos.svg
new file mode 100644
index 00000000..19949701
--- /dev/null
+++ b/src/icons/ceoos.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/ceos-chat.svg b/src/icons/ceos-chat.svg
new file mode 100644
index 00000000..027afb25
--- /dev/null
+++ b/src/icons/ceos-chat.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/ceos-design.svg b/src/icons/ceos-design.svg
new file mode 100644
index 00000000..02e12fca
--- /dev/null
+++ b/src/icons/ceos-design.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/ceos-notice.svg b/src/icons/ceos-notice.svg
new file mode 100644
index 00000000..9f6b7445
--- /dev/null
+++ b/src/icons/ceos-notice.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/ceoss.svg b/src/icons/ceoss.svg
new file mode 100644
index 00000000..63dc31e3
--- /dev/null
+++ b/src/icons/ceoss.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/chat-dark.svg b/src/icons/chat-dark.svg
new file mode 100644
index 00000000..a7cf0262
--- /dev/null
+++ b/src/icons/chat-dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/chat-white.svg b/src/icons/chat-white.svg
new file mode 100644
index 00000000..328da8ae
--- /dev/null
+++ b/src/icons/chat-white.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/connection.svg b/src/icons/connection.svg
new file mode 100644
index 00000000..e088ed24
--- /dev/null
+++ b/src/icons/connection.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/edit-white.svg b/src/icons/edit-white.svg
new file mode 100644
index 00000000..17d19387
--- /dev/null
+++ b/src/icons/edit-white.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/icons/edit.svg b/src/icons/edit.svg
new file mode 100644
index 00000000..b7c0713a
--- /dev/null
+++ b/src/icons/edit.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/friend-chat-1.svg b/src/icons/friend-chat-1.svg
new file mode 100644
index 00000000..34f86ab7
--- /dev/null
+++ b/src/icons/friend-chat-1.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/friend-chat-2.svg b/src/icons/friend-chat-2.svg
new file mode 100644
index 00000000..a318d5e5
--- /dev/null
+++ b/src/icons/friend-chat-2.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/friend-chat-3.svg b/src/icons/friend-chat-3.svg
new file mode 100644
index 00000000..965ebbfd
--- /dev/null
+++ b/src/icons/friend-chat-3.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/friend-chat-4.svg b/src/icons/friend-chat-4.svg
new file mode 100644
index 00000000..8cf95398
--- /dev/null
+++ b/src/icons/friend-chat-4.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/group-2.svg b/src/icons/group-2.svg
new file mode 100644
index 00000000..6af62758
--- /dev/null
+++ b/src/icons/group-2.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/icons/home-dark.svg b/src/icons/home-dark.svg
new file mode 100644
index 00000000..81f2d4db
--- /dev/null
+++ b/src/icons/home-dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/indicators-group.svg b/src/icons/indicators-group.svg
new file mode 100644
index 00000000..3d7611e9
--- /dev/null
+++ b/src/icons/indicators-group.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/icons/iphone-status-bar-lower.svg b/src/icons/iphone-status-bar-lower.svg
new file mode 100644
index 00000000..1f1c1255
--- /dev/null
+++ b/src/icons/iphone-status-bar-lower.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/line-img.svg b/src/icons/line-img.svg
new file mode 100644
index 00000000..a8a90386
--- /dev/null
+++ b/src/icons/line-img.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/my-profile-background.svg b/src/icons/my-profile-background.svg
new file mode 100644
index 00000000..961980eb
--- /dev/null
+++ b/src/icons/my-profile-background.svg
@@ -0,0 +1,18641 @@
+
\ No newline at end of file
diff --git a/src/icons/my-profile-big.svg b/src/icons/my-profile-big.svg
new file mode 100644
index 00000000..0f2e12be
--- /dev/null
+++ b/src/icons/my-profile-big.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/my-profile.svg b/src/icons/my-profile.svg
new file mode 100644
index 00000000..1ab4482e
--- /dev/null
+++ b/src/icons/my-profile.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/icons/report.svg b/src/icons/report.svg
new file mode 100644
index 00000000..4427371a
--- /dev/null
+++ b/src/icons/report.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icons/search.svg b/src/icons/search.svg
new file mode 100644
index 00000000..33e488d7
--- /dev/null
+++ b/src/icons/search.svg
@@ -0,0 +1,14 @@
+
diff --git a/src/icons/signal.svg b/src/icons/signal.svg
new file mode 100644
index 00000000..edd27c0b
--- /dev/null
+++ b/src/icons/signal.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/star.svg b/src/icons/star.svg
new file mode 100644
index 00000000..4fdfc2b2
--- /dev/null
+++ b/src/icons/star.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/stroke-1.svg b/src/icons/stroke-1.svg
new file mode 100644
index 00000000..439edf4c
--- /dev/null
+++ b/src/icons/stroke-1.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/video-white.svg b/src/icons/video-white.svg
new file mode 100644
index 00000000..6fcbc837
--- /dev/null
+++ b/src/icons/video-white.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 00000000..0c5d46e8
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,35 @@
+@import 'tailwindcss';
+@import './styles/colors.css';
+@import './styles/typography.css';
+
+@layer base {
+ body {
+ margin: 0;
+ font-family:
+ 'Pretendard Variable',
+ 'Pretendard',
+ system-ui,
+ -apple-system,
+ 'Segoe UI',
+ Roboto,
+ 'Noto Sans KR',
+ 'Apple SD Gothic Neo',
+ 'Helvetica Neue',
+ Arial,
+ sans-serif;
+ background: var(--gray-100);
+ color: var(--black);
+ }
+}
+
+@layer utilities {
+ /* 스크롤은 가능, 스크롤바만 숨김 */
+ .no-scrollbar::-webkit-scrollbar {
+ display: none;
+ }
+
+ .no-scrollbar {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+ }
+}
diff --git a/src/layouts/MenuTab.tsx b/src/layouts/MenuTab.tsx
new file mode 100644
index 00000000..39a3aa30
--- /dev/null
+++ b/src/layouts/MenuTab.tsx
@@ -0,0 +1,19 @@
+// src/layouts/MenuTab.tsx
+import type { ReactNode } from 'react';
+import StatusBar from '@/app/StatusBar';
+import Menu from '@/components/chatrooms/Menu';
+
+type Props = {
+ children: ReactNode;
+};
+
+export default function MenuTab({ children }: Props) {
+ return (
+
+ );
+}
diff --git a/src/layouts/MobileFrame.tsx b/src/layouts/MobileFrame.tsx
new file mode 100644
index 00000000..7f02b3b5
--- /dev/null
+++ b/src/layouts/MobileFrame.tsx
@@ -0,0 +1,24 @@
+// src/layouts/MobileFrame.tsx
+import type { ReactNode } from 'react';
+import BottomIndicator from '@/app/BottomIndicator';
+
+type Props = {
+ children: ReactNode;
+ bottomBarBg?: 'white' | 'transparent';
+};
+
+export default function MobileFrame({ children, bottomBarBg = 'white' }: Props) {
+ return (
+
+
+ {/* children 영역 */}
+
{children}
+
+ {/* 하단바 - z-index 추가하여 항상 위에 표시 */}
+
+
+
+
+
+ );
+}
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 00000000..5ed0d579
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,15 @@
+// src/main.tsx
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+// 👇 실제 프로젝트의 Provider 이름/경로에 맞춰 주세요.
+import { ChatProvider } from '@/context/ChatContext';
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+ ,
+);
diff --git a/src/pages/Call.tsx b/src/pages/Call.tsx
new file mode 100644
index 00000000..22042aa9
--- /dev/null
+++ b/src/pages/Call.tsx
@@ -0,0 +1,13 @@
+// src/pages/Call.tsx
+import MenuTab from '@/layouts/MenuTab';
+import { SyncLoader } from 'react-spinners';
+
+export default function Call() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx
new file mode 100644
index 00000000..f6fdc5ad
--- /dev/null
+++ b/src/pages/ChatRoom.tsx
@@ -0,0 +1,39 @@
+// src/pages/ChatRoom.tsx
+import { useParams, useNavigate } from 'react-router-dom';
+import StatusBar from '@/app/StatusBar';
+import HeaderBar from '@/app/HeaderBar';
+import MessageList from '@/components/chat/MessageList';
+import ChatInput from '@/components/chat/ChatInput';
+import { useLocalMessages, type TextMessage } from '@/features/chat/hooks/useLocalMessages';
+import type { User } from '@/types/chat';
+import usersData from '@/data/users.json';
+import messagesData from '@/data/messages.json';
+
+const ME_ID = 'me';
+const CHAT_ID = 'c_ceos';
+
+export default function ChatRoom() {
+ const { chatId = '' } = useParams();
+ const nav = useNavigate();
+
+ const usersById: Record = usersData;
+ const SEED: TextMessage[] = messagesData as TextMessage[];
+
+ const { messages, sendText, toggleReaction } = useLocalMessages(chatId || CHAT_ID, ME_ID, SEED);
+
+ return (
+
+
+
nav('/chats')} />
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Chats.tsx b/src/pages/Chats.tsx
new file mode 100644
index 00000000..89c19b1c
--- /dev/null
+++ b/src/pages/Chats.tsx
@@ -0,0 +1,44 @@
+// src/pages/Chats.tsx
+import { useState } from 'react';
+import MenuTab from '@/layouts/MenuTab';
+import Header from '@/components/chatrooms/Header';
+import Roomsearch from '@/components/chatrooms/Roomsearch';
+import BusinessChatList from '@/components/chatrooms/BusinessChatList';
+import FriendChatList from '@/components/chatrooms/FriendChatList';
+
+const TAB_STORAGE_KEY = 'chats-active-tab';
+
+export default function Chats() {
+ const [activeTab, setActiveTab] = useState<'friend' | 'business'>(() => {
+ const saved = localStorage.getItem(TAB_STORAGE_KEY);
+ return (saved as 'friend' | 'business') || 'friend';
+ });
+
+ const handleTabChange = (tab: 'friend' | 'business') => {
+ setActiveTab(tab);
+ localStorage.setItem(TAB_STORAGE_KEY, tab);
+ };
+
+ return (
+
+
+
+
+ {/* inline style로 스크롤바 강제 숨김 */}
+
+
+ {activeTab === 'business' ? : }
+
+
+ );
+}
diff --git a/src/pages/FriendProfile.tsx b/src/pages/FriendProfile.tsx
new file mode 100644
index 00000000..4c1af477
--- /dev/null
+++ b/src/pages/FriendProfile.tsx
@@ -0,0 +1,123 @@
+// src/pages/FriendProfile.tsx
+import { useNavigate } from 'react-router-dom';
+import StatusBar from '@/app/StatusBar';
+import { Icon } from '@/components/Icon';
+
+export default function FriendProfile() {
+ const navigate = useNavigate();
+
+ return (
+ <>
+ {/* 배경 - absolute로 전체 화면 덮기, z-index 낮게 */}
+
+
+ {/* 컨텐츠 - relative로 배경 위에 표시, z-index 높게 */}
+
+ {/* 상태바 - 흰색 */}
+
+
+ {/* 헤더 - 52px */}
+
+ {/* 왼쪽: X 아이콘 */}
+
+
+
+
+ {/* 오른쪽: 아이콘들 */}
+
+
+
+ {/* 프로필 컨텐츠 - absolute로 정확한 위치 지정 */}
+
+ {/* 프로필 사진 120x120 */}
+
+
+
+
+
+ {/* 이름 + 편집 아이콘 - 16px 간격 */}
+
+
+ 세오스
+
+
+
+
+ {/* 상태 메시지 - 4px 간격 */}
+
+ 상태 메세지를 입력해주세요
+
+
+ {/* 버튼들 - 44px 간격, 박스 사이 40px */}
+
+ {/* 대화 - 70x60 박스 */}
+
+
+ {/* 음성통화 - 70x60 박스 */}
+
+
+ {/* 영상통화 - 70x60 박스 */}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
new file mode 100644
index 00000000..792d0b51
--- /dev/null
+++ b/src/pages/Home.tsx
@@ -0,0 +1,58 @@
+// src/pages/Home.tsx
+import { useState, useEffect } from 'react';
+import MenuTab from '@/layouts/MenuTab';
+import Header from '@/components/home/Header';
+import MyProfile from '@/components/home/MyProfile';
+import Friendsearch from '@/components/home/FriendSearch';
+import MiniProfile from '@/components/home/MiniProfile';
+import FriendList from '@/components/home/FriendList';
+
+export default function Home() {
+ const [showMiniProfile, setShowMiniProfile] = useState(false);
+
+ useEffect(() => {
+ const handleScroll = () => {
+ const scrollContainer = document.querySelector('.no-scrollbar') as HTMLElement;
+ if (scrollContainer) {
+ // 스크롤이 150px 이상 내려가면 미니프로필 표시
+ setShowMiniProfile(scrollContainer.scrollTop > 150);
+ }
+ };
+
+ const scrollContainer = document.querySelector('.no-scrollbar');
+ if (scrollContainer) {
+ scrollContainer.addEventListener('scroll', handleScroll);
+ }
+
+ return () => {
+ if (scrollContainer) {
+ scrollContainer.removeEventListener('scroll', handleScroll);
+ }
+ };
+ }, []); // 빈 배열로 한 번만 실행
+
+ return (
+
+ {/* 스크롤 전: Header + MyProfile + Friendsearch 보임 */}
+ {/* 스크롤 후: 모두 숨겨짐 */}
+ {!showMiniProfile && (
+ <>
+
+
+
+
+ >
+ )}
+
+ {/* 스크롤 후: MiniProfile이 상태창 바로 아래 고정 */}
+ {showMiniProfile && (
+
+
+
+ )}
+
+ {/* 친구목록은 항상 표시 */}
+
+
+ );
+}
diff --git a/src/pages/More.tsx b/src/pages/More.tsx
new file mode 100644
index 00000000..a2125081
--- /dev/null
+++ b/src/pages/More.tsx
@@ -0,0 +1,59 @@
+// src/pages/More.tsx
+import MenuTab from '@/layouts/MenuTab';
+
+export default function More() {
+ const text = '더 많은 기능들 COMING SOON...';
+
+ return (
+
+
+ {/* 외부 고정 컨테이너 */}
+
+ {/* 내부 회전 컨테이너 */}
+
+ {text.split('').map((char, index) => (
+
+ {char === ' ' ? '\u00A0' : char}
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/MyProfile.tsx b/src/pages/MyProfile.tsx
new file mode 100644
index 00000000..a9662a42
--- /dev/null
+++ b/src/pages/MyProfile.tsx
@@ -0,0 +1,100 @@
+// src/pages/MyProfile.tsx
+import { useNavigate } from 'react-router-dom';
+import StatusBar from '@/app/StatusBar';
+import { Icon } from '@/components/Icon';
+import myProfileBackgroundUrl from '@/icons/my-profile-background.svg';
+
+export default function MyProfile() {
+ const navigate = useNavigate();
+
+ return (
+ <>
+ {/* 배경 이미지 - absolute로 전체 화면 덮기, z-index 낮게 */}
+
+
+ {/* 컨텐츠 - relative로 배경 위에 표시, z-index 높게 */}
+
+ {/* 상태바 - 흰색 */}
+
+
+ {/* 헤더 - 52px */}
+
+ {/* 왼쪽: X 아이콘 */}
+
+
+
+
+ {/* 오른쪽: 아이콘들 */}
+
+
+
+ {/* 프로필 컨텐츠 - absolute로 정확한 위치 지정 */}
+
+ {/* 프로필 사진 120x120 */}
+
+
+
+
+
+ {/* 이름 - 16px 간격 */}
+
+ 세오스
+
+
+ {/* 상태 메시지 - 4px 간격 */}
+
+ 메신저 서비스 리디자인
+
+
+ {/* 버튼들 - 44px 간격 */}
+
+ {/* 프로필 편집 */}
+
+
+ {/* Keep 메모 */}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx
new file mode 100644
index 00000000..0117e483
--- /dev/null
+++ b/src/pages/Profile.tsx
@@ -0,0 +1,23 @@
+// src/pages/Profile.tsx
+import { useParams } from 'react-router-dom';
+import type { User } from '@/types/chat';
+import usersData from '@/data/users.json';
+
+export default function Profile() {
+ const { userId } = useParams<{ userId: string }>();
+ // const navigate = useNavigate();
+
+ const usersById: Record = usersData;
+
+ // ✅ 타입 명시
+ const user = Object.values(usersById).find((u: User) => u.id === userId);
+
+ // 또는
+ //const user2 = Object.entries(usersById).find(([id, u]: [string, User]) => id === userId)?.[1];
+
+ if (!user) {
+ return 사용자를 찾을 수 없습니다.
;
+ }
+
+ return {/* 프로필 내용 */}
;
+}
diff --git a/src/styles/colors.css b/src/styles/colors.css
new file mode 100644
index 00000000..39ea45db
--- /dev/null
+++ b/src/styles/colors.css
@@ -0,0 +1,30 @@
+/* =========================
+ Design Tokens — Palette
+ (from Figma)
+ ========================= */
+
+:root {
+ /* ===== basic ===== */
+ --black: #222222;
+ --white: #ffffff;
+
+ /* ===== gray scale ===== */
+ --gray-100: #f8f8f9;
+ --gray-200: #efeff1;
+ --gray-300: #e7e7ea;
+ --gray-400: #ceced4;
+ --gray-500: #a5a5ae;
+ --gray-600: #626273;
+ --gray-700: #4e4e5c;
+ --gray-800: #3b3b45;
+ --gray-900: #2c2c33;
+
+ /* ===== main color — green ===== */
+ --green-100: #f0faf4;
+ --green-200: #daf7e6;
+ --green-300: #06c755;
+ --green-400: #059f44;
+
+ /* ===== sub color — blue ===== */
+ --blue-100: #c8e9fa;
+}
diff --git a/src/styles/typography.css b/src/styles/typography.css
new file mode 100644
index 00000000..8e4c2222
--- /dev/null
+++ b/src/styles/typography.css
@@ -0,0 +1,73 @@
+/* =========================================
+ Typography Tokens (Pretendard)
+ - Figma 명세 기반
+ - line-height: %, letter-spacing: -0.3% = -0.003em
+ ========================================= */
+
+/* Titles */
+.text-title-1 {
+ font-size: 24px;
+ line-height: 130%;
+ letter-spacing: -0.003em;
+ font-weight: 600;
+} /* Semibold 24 */
+.text-title-2 {
+ font-size: 20px;
+ line-height: 130%;
+ letter-spacing: -0.003em;
+ font-weight: 600;
+} /* Semibold 20 */
+.text-title-3 {
+ font-size: 20px;
+ line-height: 130%;
+ letter-spacing: -0.003em;
+ font-weight: 400;
+} /* Regular 20 */
+
+/* Body 1 (16px) */
+.text-body1-bold {
+ font-size: 16px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 600;
+}
+.text-body1-medium {
+ font-size: 16px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 500;
+}
+.text-body1-regular {
+ font-size: 16px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 400;
+}
+
+/* Body 2 (14px) */
+.text-body2-bold {
+ font-size: 14px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 600;
+}
+.text-body2-medium {
+ font-size: 14px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 500;
+}
+.text-body2-regular {
+ font-size: 14px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 400;
+}
+
+/* Caption (12px) */
+.text-caption-medium {
+ font-size: 12px;
+ line-height: 140%;
+ letter-spacing: -0.003em;
+ font-weight: 500;
+} /* Medium 12 */
diff --git a/src/types/chat.ts b/src/types/chat.ts
new file mode 100644
index 00000000..a68053da
--- /dev/null
+++ b/src/types/chat.ts
@@ -0,0 +1,38 @@
+// src/types/chat.ts
+export type User = {
+ id: string;
+ name: string;
+ avatarUrl: string;
+};
+
+export type TextMessage = {
+ id: string;
+ kind: 'text';
+ chatId: string;
+ userId: string;
+ text: string;
+ createdAt: string;
+ reaction?: '❤️' | null;
+};
+
+export type Message = TextMessage;
+export type Id = string;
+
+// ChatContext 전용
+export type Conversation = {
+ id: string;
+ title: string;
+ memberCount: number;
+ participantIds: string[];
+};
+
+// 채팅 목록 전용
+export type ChatListItem = {
+ id: string;
+ name: string;
+ lastMessage: string;
+ time: string;
+ unreadCount: number;
+ avatarUrl: string;
+ type: 'friend' | 'business';
+};
diff --git a/tailwind.config.ts b/tailwind.config.ts
new file mode 100644
index 00000000..2427de57
--- /dev/null
+++ b/tailwind.config.ts
@@ -0,0 +1,30 @@
+import type { Config } from 'tailwindcss';
+
+export default {
+ content: ['./index.html', './src/**/*.{ts,tsx}'],
+ darkMode: 'class', // (선택) 다크모드 class 기반
+ theme: {
+ extend: {
+ // Pretendard를 기본 sans로 사용
+ fontFamily: {
+ sans: [
+ 'Pretendard Variable',
+ 'Pretendard',
+ 'system-ui',
+ '-apple-system',
+ 'Segoe UI',
+ 'Roboto',
+ 'Noto Sans KR',
+ 'Apple SD Gothic Neo',
+ 'Helvetica Neue',
+ 'Arial',
+ 'sans-serif',
+ ],
+ },
+ // (선택) 자주 쓰는 radius/shadow만 유틸화
+ borderRadius: { bubble: '16px', card: '14px' },
+ boxShadow: { card: '0 4px 14px rgba(0,0,0,0.08)' },
+ },
+ },
+ plugins: [],
+} satisfies Config;
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 00000000..b097ab13
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,38 @@
+//tsconfig.app.json
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Path alias for "@/..." */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ },
+
+ /* Nice-to-have */
+ "resolveJsonModule": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..03c6f28a
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,5 @@
+//tsconfig.json
+{
+ "files": [],
+ "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 00000000..7a77bab3
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "types": [],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vercel.json b/vercel.json
new file mode 100644
index 00000000..3a48e56b
--- /dev/null
+++ b/vercel.json
@@ -0,0 +1,3 @@
+{
+ "rewrites": [{ "source": "/(.*)", "destination": "/" }]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 00000000..b539f975
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import tailwindcss from '@tailwindcss/vite';
+
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+ resolve: { alias: { '@': '/src' } },
+});