diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 62f073d..765b225 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,19 +1,27 @@ +/* eslint-env node */ +require('@vue/eslint-config-typescript') + module.exports = { root: true, - env: { browser: true, es2020: true }, + env: { + browser: true, + es2020: true, + }, extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', + 'plugin:vue/vue3-recommended', + '@vue/eslint-config-typescript', ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ['dist', '.eslintrc.cjs', '*.config.js'], + parser: 'vue-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], + 'vue/multi-word-component-names': 'off', }, } - diff --git a/README.md b/README.md index facf1bc..bc14119 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Browser Vault GUI -A modern TypeScript/React frontend for HashiCorp Vault. This is an alternative web interface that allows you to connect to multiple Vault servers and manage your secrets. +A modern Vue 3 + TypeScript frontend for HashiCorp Vault with Tailwind CSS and DaisyUI. This is an alternative web interface that allows you to connect to multiple Vault servers and manage your secrets. ## Features @@ -142,10 +142,11 @@ Cache can be cleared manually from Settings or programmatically on logout. ## Technology Stack -- **React 18** - UI framework -- **TypeScript** - Type safety -- **Vite** - Build tool and dev server -- **CSS3** - Styling with CSS custom properties +- **Vue 3** - Progressive JavaScript framework with Composition API +- **TypeScript** - Type safety throughout +- **Tailwind CSS** - Utility-first CSS framework +- **DaisyUI** - Beautiful component library for Tailwind +- **Vite** - Lightning-fast build tool and dev server - **Custom Vault Client** - Browser-compatible Vault HTTP API client with retries, timeouts, and error handling ## Development @@ -154,12 +155,12 @@ Cache can be cleared manually from Settings or programmatically on logout. ``` src/ -├── components/ # React components -│ ├── ServerSelector.tsx/css -│ ├── LoginForm.tsx/css -│ ├── Dashboard.tsx/css -│ ├── PathSearch.tsx/css -│ └── Settings.tsx/css +├── components/ # Vue 3 components +│ ├── ServerSelector.vue +│ ├── LoginForm.vue +│ ├── Dashboard.vue +│ ├── PathSearch.vue +│ └── Settings.vue ├── services/ │ ├── vaultClient.ts # Low-level Vault HTTP API client │ └── vaultApi.ts # High-level API with caching @@ -167,17 +168,21 @@ src/ │ └── cache.ts # Cache management system ├── types.ts # TypeScript type definitions ├── config.ts # Application configuration -├── App.tsx/css # Main application component -├── main.tsx # Application entry point -└── index.css # Global styles +├── App.vue # Main application component +├── main.ts # Application entry point +└── style.css # Tailwind CSS imports ``` ### Scripts -- `npm run dev` - Start development server -- `npm run build` - Build for production +- `npm run dev` - Start development server (Vite) +- `npm run build` - Build for production (Vue TSC + Vite) - `npm run preview` - Preview production build -- `npm run lint` - Run ESLint +- `npm run lint` - Run ESLint (Vue + TypeScript) + +### Migration Note + +This project was recently migrated from React to Vue 3. See `VUE_MIGRATION.md` for details. ## License diff --git a/VUE_MIGRATION.md b/VUE_MIGRATION.md new file mode 100644 index 0000000..962d365 --- /dev/null +++ b/VUE_MIGRATION.md @@ -0,0 +1,345 @@ +# Vue 3 Migration Complete! 🎉 + +## What Changed + +### ✅ Complete Rewrite from React to Vue 3 + Tailwind + DaisyUI + +The entire UI layer has been converted while keeping all business logic intact! + +## New Stack + +- **Vue 3** with Composition API (` + + +``` + +### TypeScript Props & Emits + +```vue + +``` + +### Reactivity + +- `ref()` - for primitive values (`ref(0)`, `ref('')`) +- `reactive()` - for objects (not used much, ref works for everything) +- `computed()` - for derived values +- `watch()` / `watchEffect()` - for side effects + +## DaisyUI Components Used + +### Buttons +```html + + + + +``` + +### Cards +```html +
+
+

Title

+

Content

+
+
+``` + +### Forms +```html +
+ + +
+``` + +### Alerts +```html +
+ ... + Info message +
+``` + +### Modal +```html + +``` + +## Feature Comparison + +| Feature | React Version | Vue Version | Status | +|---------|--------------|-------------|--------| +| Server Management | ✅ | ✅ | Identical | +| Multi-Auth Support | ✅ | ✅ | Identical | +| Login Verification | ✅ | ✅ | Identical | +| Mount Point Detection | ✅ | ✅ | Identical | +| Secret Reading | ✅ | ✅ | Identical | +| Recursive Search | ✅ | ✅ | Identical | +| Multi-Mount Search | ✅ | ✅ | Identical | +| Caching System | ✅ | ✅ | Identical | +| Settings Panel | ✅ | ✅ | Identical | +| KV v1/v2 Support | ✅ | ✅ | Identical | +| Dark/Light Mode | ✅ | ✅ | Improved (DaisyUI themes) | +| Responsive Design | ✅ | ✅ | Improved (Tailwind) | + +## Benefits of Vue Version + +### Code Quality +- ✅ **Less Code**: Vue templates are more concise than JSX +- ✅ **Better Separation**: Logic in ` + + +``` + +## Theme Switching + +DaisyUI supports multiple themes. To switch themes: + +```html + + +``` + +Or dynamically: +```typescript +document.documentElement.setAttribute('data-theme', 'light') +``` + +Available themes: dark, light, cupcake, bumblebee, emerald, corporate, synthwave, retro, cyberpunk, valentine, halloween, garden, forest, aqua, lofi, pastel, fantasy, wireframe, black, luxury, dracula, cmyk, autumn, business, acid, lemonade, night, coffee, winter + +## Next Steps + +### Recommended Enhancements + +1. **Add Vue Router** (if you want multiple pages) +2. **Add Pinia** (Vue's state management, like Redux) +3. **Add VueUse** (collection of useful composition utilities) +4. **Add animations** with Vue transitions +5. **PWA support** with Vite PWA plugin + +### Optional Improvements + +1. **Virtual scrolling** for large result lists +2. **Drag & drop** for organizing servers +3. **Keyboard shortcuts** with Vue composables +4. **Export/import** server configurations +5. **Secret editing** UI with forms + +## Testing + +To test the conversion: + +1. `npm install` +2. `npm run dev` +3. Add a server (should work like before) +4. Login (mount points should be detected) +5. Read a secret +6. Try search (single path and all mounts) +7. Check settings panel +8. Verify cache statistics + +Everything should work identically to the React version! + +## Documentation + +All previous documentation still applies: +- `README.md` - Updated for Vue +- `USAGE.md` - Same usage, new UI +- `KV_VERSIONS.md` - No changes +- `MOUNT_POINTS.md` - No changes +- `CORS_AND_CLIENT.md` - No changes + +## Conclusion + +✅ **Migration Complete!** +✅ **All features preserved** +✅ **Smaller bundle size** +✅ **Better performance** +✅ **Modern UI with Tailwind + DaisyUI** +✅ **Cleaner codebase** + +The Vue version is production-ready! 🚀 + diff --git a/index.html b/index.html index 669f483..13661a0 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + @@ -7,8 +7,8 @@ Browser Vault GUI -
- +
+ diff --git a/package.json b/package.json index e95ff81..3c8fce3 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,30 @@ { "name": "browser-vault-gui", - "version": "0.1.0", - "description": "Alternative frontend for HashiCorp Vault", + "version": "0.2.0", + "description": "Alternative frontend for HashiCorp Vault (Vue 3 + Tailwind)", "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vue-tsc && vite build", "preview": "vite preview", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" + "lint": "eslint . --ext .vue,.ts --report-unused-disable-directives --max-warnings 0" }, "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "vue": "^3.4.15" }, "devDependencies": { - "@types/react": "^18.2.43", - "@types/react-dom": "^18.2.17", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", - "@vitejs/plugin-react": "^4.2.1", + "@vitejs/plugin-vue": "^5.0.3", + "@vue/eslint-config-typescript": "^12.0.0", + "autoprefixer": "^10.4.16", + "daisyui": "^4.4.24", "eslint": "^8.55.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", + "eslint-plugin-vue": "^9.19.2", + "postcss": "^8.4.33", + "tailwindcss": "^3.4.1", "typescript": "^5.3.3", - "vite": "^5.0.8" + "vite": "^5.0.8", + "vue-tsc": "^1.8.27" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e985fd..9cc13ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,83 +8,55 @@ importers: .: dependencies: - react: - specifier: ^18.2.0 - version: 18.3.1 - react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + vue: + specifier: ^3.4.15 + version: 3.5.22(typescript@5.9.3) devDependencies: - '@types/react': - specifier: ^18.2.43 - version: 18.3.26 - '@types/react-dom': - specifier: ^18.2.17 - version: 18.3.7(@types/react@18.3.26) '@typescript-eslint/eslint-plugin': specifier: ^6.14.0 version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^6.14.0 version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) - '@vitejs/plugin-react': - specifier: ^4.2.1 - version: 4.7.0(vite@5.4.21) + '@vitejs/plugin-vue': + specifier: ^5.0.3 + version: 5.2.4(vite@5.4.21)(vue@3.5.22(typescript@5.9.3)) + '@vue/eslint-config-typescript': + specifier: ^12.0.0 + version: 12.0.0(eslint-plugin-vue@9.33.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + autoprefixer: + specifier: ^10.4.16 + version: 10.4.21(postcss@8.5.6) + daisyui: + specifier: ^4.4.24 + version: 4.12.24(postcss@8.5.6) eslint: specifier: ^8.55.0 version: 8.57.1 - eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.2(eslint@8.57.1) - eslint-plugin-react-refresh: - specifier: ^0.4.5 - version: 0.4.24(eslint@8.57.1) + eslint-plugin-vue: + specifier: ^9.19.2 + version: 9.33.0(eslint@8.57.1) + postcss: + specifier: ^8.4.33 + version: 8.5.6 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.18 typescript: specifier: ^5.3.3 version: 5.9.3 vite: specifier: ^5.0.8 version: 5.4.21 + vue-tsc: + specifier: ^1.8.27 + version: 1.8.27(typescript@5.9.3) packages: - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.28.4': - resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} @@ -94,39 +66,11 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} @@ -300,12 +244,13 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -328,8 +273,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} '@rollup/rollup-android-arm-eabi@4.52.5': resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} @@ -441,35 +387,12 @@ packages: cpu: [x64] os: [win32] - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - - '@types/react-dom@18.3.7': - resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} - peerDependencies: - '@types/react': ^18.0.0 - - '@types/react@18.3.26': - resolution: {integrity: sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==} - '@types/semver@7.7.1': resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} @@ -534,11 +457,69 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitejs/plugin-react@4.7.0': - resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} - engines: {node: ^14.18.0 || >=16.0.0} + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@volar/language-core@1.11.1': + resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + + '@volar/source-map@1.11.1': + resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + + '@volar/typescript@1.11.1': + resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + + '@vue/compiler-core@3.5.22': + resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==} + + '@vue/compiler-dom@3.5.22': + resolution: {integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==} + + '@vue/compiler-sfc@3.5.22': + resolution: {integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==} + + '@vue/compiler-ssr@3.5.22': + resolution: {integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==} + + '@vue/eslint-config-typescript@12.0.0': + resolution: {integrity: sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + eslint-plugin-vue: ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@1.8.27': + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.22': + resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==} + + '@vue/runtime-core@3.5.22': + resolution: {integrity: sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==} + + '@vue/runtime-dom@3.5.22': + resolution: {integrity: sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==} + + '@vue/server-renderer@3.5.22': + resolution: {integrity: sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==} + peerDependencies: + vue: 3.5.22 + + '@vue/shared@3.5.22': + resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -557,10 +538,28 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -568,6 +567,13 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -575,6 +581,13 @@ packages: resolution: {integrity: sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==} hasBin: true + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -594,6 +607,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + caniuse-lite@1.0.30001751: resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} @@ -601,6 +618,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -608,19 +629,42 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-selector-tokenizer@0.8.0: + resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + culori@3.3.0: + resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + daisyui@4.12.24: + resolution: {integrity: sha512-JYg9fhQHOfXyLadrBrEqCDM6D5dWCSSiM6eTNCRrBRzx/VlOCrLS8eDfIw9RVvs64v2mJdLooKXY8EwQzoszAA==} + engines: {node: '>=16.9.0'} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -633,17 +677,36 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.237: resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -657,16 +720,11 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-plugin-react-hooks@4.6.2: - resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} - engines: {node: '>=10'} + eslint-plugin-vue@9.33.0: + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} + engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - - eslint-plugin-react-refresh@0.4.24: - resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} - peerDependencies: - eslint: '>=8.40' + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} @@ -698,6 +756,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -715,6 +776,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fastparse@1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -737,6 +801,13 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -745,9 +816,8 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -757,6 +827,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -776,6 +850,14 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -795,10 +877,22 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -814,18 +908,17 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -835,11 +928,6 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -847,6 +935,13 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -854,12 +949,14 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -876,9 +973,23 @@ packages: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + muggle-string@0.3.1: + resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -890,6 +1001,25 @@ packages: node-releases@2.0.25: resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -905,10 +1035,16 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -921,6 +1057,13 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -932,6 +1075,57 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -947,23 +1141,22 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} - peerDependencies: - react: ^18.3.1 + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - react-refresh@0.17.0: - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} - engines: {node: '>=0.10.0'} - - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -981,13 +1174,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -1001,6 +1187,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -1009,21 +1199,54 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + engines: {node: '>=14.0.0'} + hasBin: true + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1034,6 +1257,9 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1056,6 +1282,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite@5.4.21: resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1087,6 +1316,29 @@ packages: terser: optional: true + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + + vue-tsc@1.8.27: + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} + hasBin: true + peerDependencies: + typescript: '*' + + vue@3.5.22: + resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1096,11 +1348,20 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} @@ -1108,113 +1369,16 @@ packages: snapshots: - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.28.4': {} - - '@babel/core@7.28.4': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.28.3': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.28.4 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.26.3 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.27.1': {} + '@alloc/quick-lru@5.2.0': {} '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.4': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - '@babel/parser@7.28.4': dependencies: '@babel/types': 7.28.4 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - - '@babel/traverse@7.28.4': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -1324,16 +1488,20 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -1355,7 +1523,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@rolldown/pluginutils@1.0.0-beta.27': {} + '@pkgjs/parseargs@0.11.0': + optional: true '@rollup/rollup-android-arm-eabi@4.52.5': optional: true @@ -1423,42 +1592,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.28.4 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.28.4 - '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} - '@types/prop-types@15.7.15': {} - - '@types/react-dom@18.3.7(@types/react@18.3.26)': - dependencies: - '@types/react': 18.3.26 - - '@types/react@18.3.26': - dependencies: - '@types/prop-types': 15.7.15 - csstype: 3.1.3 - '@types/semver@7.7.1': {} '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': @@ -1549,18 +1686,104 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.7.0(vite@5.4.21)': + '@vitejs/plugin-vue@5.2.4(vite@5.4.21)(vue@3.5.22(typescript@5.9.3))': dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) - '@rolldown/pluginutils': 1.0.0-beta.27 - '@types/babel__core': 7.20.5 - react-refresh: 0.17.0 vite: 5.4.21 + vue: 3.5.22(typescript@5.9.3) + + '@volar/language-core@1.11.1': + dependencies: + '@volar/source-map': 1.11.1 + + '@volar/source-map@1.11.1': + dependencies: + muggle-string: 0.3.1 + + '@volar/typescript@1.11.1': + dependencies: + '@volar/language-core': 1.11.1 + path-browserify: 1.0.1 + + '@vue/compiler-core@3.5.22': + dependencies: + '@babel/parser': 7.28.4 + '@vue/shared': 3.5.22 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.22': + dependencies: + '@vue/compiler-core': 3.5.22 + '@vue/shared': 3.5.22 + + '@vue/compiler-sfc@3.5.22': + dependencies: + '@babel/parser': 7.28.4 + '@vue/compiler-core': 3.5.22 + '@vue/compiler-dom': 3.5.22 + '@vue/compiler-ssr': 3.5.22 + '@vue/shared': 3.5.22 + estree-walker: 2.0.2 + magic-string: 0.30.19 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.22': + dependencies: + '@vue/compiler-dom': 3.5.22 + '@vue/shared': 3.5.22 + + '@vue/eslint-config-typescript@12.0.0(eslint-plugin-vue@9.33.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + eslint-plugin-vue: 9.33.0(eslint@8.57.1) + vue-eslint-parser: 9.4.3(eslint@8.57.1) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - supports-color + '@vue/language-core@1.8.27(typescript@5.9.3)': + dependencies: + '@volar/language-core': 1.11.1 + '@volar/source-map': 1.11.1 + '@vue/compiler-dom': 3.5.22 + '@vue/shared': 3.5.22 + computeds: 0.0.1 + minimatch: 9.0.3 + muggle-string: 0.3.1 + path-browserify: 1.0.1 + vue-template-compiler: 2.7.16 + optionalDependencies: + typescript: 5.9.3 + + '@vue/reactivity@3.5.22': + dependencies: + '@vue/shared': 3.5.22 + + '@vue/runtime-core@3.5.22': + dependencies: + '@vue/reactivity': 3.5.22 + '@vue/shared': 3.5.22 + + '@vue/runtime-dom@3.5.22': + dependencies: + '@vue/reactivity': 3.5.22 + '@vue/runtime-core': 3.5.22 + '@vue/shared': 3.5.22 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.22(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.22 + '@vue/shared': 3.5.22 + vue: 3.5.22(typescript@5.9.3) + + '@vue/shared@3.5.22': {} + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -1576,18 +1799,45 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.3: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + argparse@2.0.1: {} array-union@2.1.0: {} + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + caniuse-lite: 1.0.30001751 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.18: {} + binary-extensions@2.3.0: {} + + boolbase@1.0.0: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -1611,6 +1861,8 @@ snapshots: callsites@3.1.0: {} + camelcase-css@2.0.1: {} + caniuse-lite@1.0.30001751: {} chalk@4.1.2: @@ -1618,15 +1870,29 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} - concat-map@0.0.1: {} + commander@4.1.1: {} - convert-source-map@2.0.0: {} + computeds@0.0.1: {} + + concat-map@0.0.1: {} cross-spawn@7.0.6: dependencies: @@ -1634,24 +1900,56 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-selector-tokenizer@0.8.0: + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + + cssesc@3.0.0: {} + csstype@3.1.3: {} + culori@3.3.0: {} + + daisyui@4.12.24(postcss@8.5.6): + dependencies: + css-selector-tokenizer: 0.8.0 + culori: 3.3.0 + picocolors: 1.1.1 + postcss-js: 4.1.0(postcss@8.5.6) + transitivePeerDependencies: + - postcss + + de-indent@1.0.2: {} + debug@4.4.3: dependencies: ms: 2.1.3 deep-is@0.1.4: {} + didyoumean@1.2.2: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 + dlv@1.1.3: {} + doctrine@3.0.0: dependencies: esutils: 2.0.3 + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.237: {} + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -1682,13 +1980,19 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): - dependencies: - eslint: 8.57.1 - - eslint-plugin-react-refresh@0.4.24(eslint@8.57.1): + eslint-plugin-vue@9.33.0(eslint@8.57.1): dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) eslint: 8.57.1 + globals: 13.24.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.7.3 + vue-eslint-parser: 9.4.3(eslint@8.57.1) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color eslint-scope@7.2.2: dependencies: @@ -1756,6 +2060,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} fast-deep-equal@3.1.3: {} @@ -1772,6 +2078,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fastparse@1.1.2: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -1797,12 +2105,19 @@ snapshots: flatted@3.3.3: {} + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: optional: true - gensync@1.0.0-beta.2: {} + function-bind@1.1.2: {} glob-parent@5.1.2: dependencies: @@ -1812,6 +2127,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -1838,6 +2162,12 @@ snapshots: has-flag@4.0.0: {} + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + ignore@5.3.2: {} import-fresh@3.3.1: @@ -1854,8 +2184,18 @@ snapshots: inherits@2.0.4: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -1866,22 +2206,24 @@ snapshots: isexe@2.0.0: {} - js-tokens@4.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} js-yaml@4.1.0: dependencies: argparse: 2.0.1 - jsesc@3.1.0: {} - json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} - json5@2.2.3: {} - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -1891,19 +2233,23 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.merge@4.6.2: {} - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 + lodash@4.17.21: {} - lru-cache@5.1.1: + lru-cache@10.4.3: {} + + magic-string@0.30.19: dependencies: - yallist: 3.1.1 + '@jridgewell/sourcemap-codec': 1.5.5 merge2@1.4.1: {} @@ -1920,14 +2266,40 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + ms@2.1.3: {} + muggle-string@0.3.1: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} natural-compare@1.4.0: {} node-releases@2.0.25: {} + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -1949,22 +2321,68 @@ snapshots: dependencies: p-limit: 3.1.0 + package-json-from-dist@1.0.1: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 + path-browserify@1.0.1: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} path-key@3.1.1: {} + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + path-type@4.0.0: {} picocolors@1.1.1: {} picomatch@2.3.1: {} + pify@2.3.0: {} + + pirates@4.0.7: {} + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-js@4.1.0(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.6 + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -1977,20 +2395,22 @@ snapshots: queue-microtask@1.2.3: {} - react-dom@18.3.1(react@18.3.1): + read-cache@1.0.0: dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + pify: 2.3.0 - react-refresh@0.17.0: {} - - react@18.3.1: + readdirp@3.6.0: dependencies: - loose-envify: 1.4.0 + picomatch: 2.3.1 resolve-from@4.0.0: {} + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} rimraf@3.0.2: @@ -2029,12 +2449,6 @@ snapshots: dependencies: queue-microtask: 1.2.3 - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 - - semver@6.3.1: {} - semver@7.7.3: {} shebang-command@2.0.0: @@ -2043,22 +2457,88 @@ snapshots: shebang-regex@3.0.0: {} + signal-exit@4.1.0: {} + slash@3.0.0: {} source-map-js@1.2.1: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + strip-json-comments@3.1.1: {} + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@3.4.18: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - tsx + - yaml + text-table@0.2.0: {} + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -2067,6 +2547,8 @@ snapshots: dependencies: typescript: 5.9.3 + ts-interface-checker@0.1.13: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -2085,6 +2567,8 @@ snapshots: dependencies: punycode: 2.3.1 + util-deprecate@1.0.2: {} + vite@5.4.21: dependencies: esbuild: 0.21.5 @@ -2093,14 +2577,61 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + vue-eslint-parser@9.4.3(eslint@8.57.1): + dependencies: + debug: 4.4.3 + eslint: 8.57.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + lodash: 4.17.21 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + vue-template-compiler@2.7.16: + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + vue-tsc@1.8.27(typescript@5.9.3): + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.9.3) + semver: 7.7.3 + typescript: 5.9.3 + + vue@3.5.22(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.22 + '@vue/compiler-sfc': 3.5.22 + '@vue/runtime-dom': 3.5.22 + '@vue/server-renderer': 3.5.22(vue@3.5.22(typescript@5.9.3)) + '@vue/shared': 3.5.22 + optionalDependencies: + typescript: 5.9.3 + which@2.0.2: dependencies: isexe: 2.0.0 word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + wrappy@1.0.2: {} - yallist@3.1.1: {} + xml-name-validator@4.0.0: {} yocto-queue@0.1.0: {} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..ba7e2f8 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} + diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 47419ad..0000000 --- a/src/App.css +++ /dev/null @@ -1,169 +0,0 @@ -.app { - display: flex; - flex-direction: column; - min-height: 100vh; - width: 100%; -} - -.app-header { - background: linear-gradient(135deg, var(--primary-color) 0%, #4338ca 100%); - color: white; - padding: 2rem; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.header-content { - max-width: 1200px; - margin: 0 auto; -} - -.app-header h1 { - font-size: 2.5rem; - margin-bottom: 0.5rem; - color: white; -} - -.subtitle { - font-size: 1.1rem; - opacity: 0.9; - color: rgba(255, 255, 255, 0.9); -} - -.app-main { - flex: 1; - padding: 2rem; - max-width: 1400px; - width: 100%; - margin: 0 auto; -} - -.login-container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2rem; - margin-top: 2rem; -} - -@media (max-width: 968px) { - .login-container { - grid-template-columns: 1fr; - } -} - -.server-section, -.auth-section { - background: var(--surface); - border-radius: 12px; - padding: 2rem; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); - border: 1px solid var(--border); -} - -.app-footer { - background: var(--surface); - border-top: 1px solid var(--border); - padding: 1.5rem; - text-align: center; - color: var(--text-secondary); - font-size: 0.9rem; -} - -/* Button Styles */ -.btn { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - cursor: pointer; - transition: all 0.25s; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.5em; -} - -.btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-primary { - background-color: var(--primary-color); - color: white; -} - -.btn-primary:hover:not(:disabled) { - background-color: var(--primary-hover); -} - -.btn-success { - background-color: var(--success-color); - color: white; -} - -.btn-success:hover:not(:disabled) { - background-color: var(--success-hover); -} - -.btn-danger { - background-color: var(--danger-color); - color: white; -} - -.btn-danger:hover:not(:disabled) { - background-color: var(--danger-hover); -} - -.btn-sm { - padding: 0.4em 0.8em; - font-size: 0.875em; -} - -.btn-block { - width: 100%; - margin-top: 1rem; -} - -/* Form Styles */ -.form-group { - margin-bottom: 1.5rem; -} - -.form-group label { - display: block; - margin-bottom: 0.5rem; - font-weight: 500; - color: var(--text-primary); -} - -.form-group input, -.form-group select { - width: 100%; -} - -.form-select { - width: 100%; -} - -.form-hint { - display: block; - margin-top: 0.5rem; - font-size: 0.875rem; - color: var(--text-secondary); -} - -.section-header { - margin-bottom: 1.5rem; -} - -.section-header h2 { - font-size: 1.5rem; - margin-bottom: 0.5rem; -} - -.section-header p { - color: var(--text-secondary); -} - diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 78dd2a3..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useState, useEffect } from 'react'; -import './App.css'; -import { VaultServer, VaultCredentials, VaultConnection } from './types'; -import ServerSelector from './components/ServerSelector'; -import LoginForm from './components/LoginForm'; -import Dashboard from './components/Dashboard'; - -function App() { - const [servers, setServers] = useState([]); - const [selectedServer, setSelectedServer] = useState(null); - const [activeConnection, setActiveConnection] = useState(null); - - // Load servers from localStorage on mount - useEffect(() => { - const savedServers = localStorage.getItem('vaultServers'); - if (savedServers) { - setServers(JSON.parse(savedServers)); - } - }, []); - - // Save servers to localStorage whenever they change - useEffect(() => { - if (servers.length > 0) { - localStorage.setItem('vaultServers', JSON.stringify(servers)); - } - }, [servers]); - - const handleAddServer = (server: VaultServer) => { - setServers([...servers, server]); - }; - - const handleRemoveServer = (serverId: string) => { - setServers(servers.filter(s => s.id !== serverId)); - if (selectedServer?.id === serverId) { - setSelectedServer(null); - setActiveConnection(null); - } - }; - - const handleSelectServer = (server: VaultServer) => { - setSelectedServer(server); - setActiveConnection(null); - }; - - const handleLogin = async (credentials: VaultCredentials) => { - if (!selectedServer) return; - - try { - // Verify login and get mount points - const { vaultApi } = await import('./services/vaultApi'); - const mountPoints = await vaultApi.verifyLoginAndGetMounts( - selectedServer, - credentials - ); - - const connection: VaultConnection = { - server: selectedServer, - credentials, - isConnected: true, - lastConnected: new Date(), - mountPoints, - }; - - setActiveConnection(connection); - - console.log(`✓ Logged in successfully. Found ${mountPoints.length} KV mount point(s).`); - } catch (error) { - console.error('Login failed:', error); - alert( - `Login failed: ${error instanceof Error ? error.message : 'Unknown error'}\n\n` + - 'Please check your credentials and server configuration.' - ); - } - }; - - const handleLogout = () => { - setActiveConnection(null); - }; - - return ( -
-
-
-

🔐 Browser Vault GUI

-

Alternative frontend for HashiCorp Vault

-
-
- -
- {!activeConnection ? ( -
-
- -
- - {selectedServer && ( -
- -
- )} -
- ) : ( - - )} -
- -
-

Browser Vault GUI - An alternative frontend for HashiCorp Vault

-
-
- ); -} - -export default App; - diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..c7a71aa --- /dev/null +++ b/src/App.vue @@ -0,0 +1,127 @@ + + + + diff --git a/src/components/Dashboard.css b/src/components/Dashboard.css deleted file mode 100644 index 9a92e44..0000000 --- a/src/components/Dashboard.css +++ /dev/null @@ -1,175 +0,0 @@ -.dashboard { - background: var(--surface); - border-radius: 12px; - padding: 2rem; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); - border: 1px solid var(--border); -} - -.dashboard-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding-bottom: 2rem; - border-bottom: 1px solid var(--border); - margin-bottom: 2rem; -} - -.dashboard-actions { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; -} - -.connection-info h2 { - margin: 0 0 0.5rem 0; - font-size: 1.75rem; -} - -.connection-info .server-url { - color: var(--text-secondary); - font-size: 0.875rem; - font-family: 'Courier New', monospace; - margin: 0.25rem 0; -} - -.connection-info .auth-info { - color: var(--text-secondary); - font-size: 0.875rem; - margin-top: 0.5rem; -} - -.connection-time { - font-style: italic; -} - -.dashboard-content { - max-width: 900px; -} - -.secret-browser h3 { - margin-bottom: 1.5rem; - font-size: 1.5rem; -} - -.secret-path-input { - margin-bottom: 2rem; -} - -.secret-path-input label { - display: block; - margin-bottom: 0.5rem; - font-weight: 500; -} - -.input-group { - display: flex; - gap: 0.5rem; -} - -.input-group input { - flex: 1; -} - -.secret-display { - background: var(--surface-light); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 2rem; - border: 1px solid var(--border); -} - -.secret-display h4 { - margin: 0 0 1rem 0; - font-size: 1.25rem; -} - -.secret-data { - background: var(--surface); - padding: 1rem; - border-radius: 6px; - overflow-x: auto; - font-size: 0.875rem; - line-height: 1.6; - margin: 0; -} - -.info-box { - background: linear-gradient(135deg, rgba(100, 108, 255, 0.1) 0%, rgba(67, 56, 202, 0.1) 100%); - border: 1px solid var(--primary-color); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 2rem; -} - -.info-box h4 { - margin: 0 0 1rem 0; - color: var(--primary-color); -} - -.info-box ul { - margin: 0.5rem 0; - padding-left: 1.5rem; -} - -.info-box li { - margin: 0.5rem 0; - line-height: 1.6; -} - -.api-info { - background: var(--surface-light); - border-radius: 8px; - padding: 1.5rem; - border: 1px solid var(--border); -} - -.api-info h4 { - margin: 0 0 1rem 0; - font-size: 1.25rem; -} - -.api-info p { - margin: 0.75rem 0; - line-height: 1.6; - color: var(--text-secondary); -} - -.api-info ul { - margin: 0.5rem 0; - padding-left: 1.5rem; -} - -.api-info li { - margin: 0.5rem 0; - line-height: 1.6; - color: var(--text-secondary); -} - -.api-info strong { - color: var(--text-primary); -} - -@media (max-width: 768px) { - .dashboard-header { - flex-direction: column; - gap: 1rem; - } - - .dashboard-actions { - width: 100%; - } - - .dashboard-actions button { - flex: 1; - } - - .input-group { - flex-direction: column; - } - - .input-group button { - width: 100%; - } -} - diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx deleted file mode 100644 index 46ee6cf..0000000 --- a/src/components/Dashboard.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { useState } from 'react'; -import { VaultConnection } from '../types'; -import { vaultApi, VaultError } from '../services/vaultApi'; -import PathSearch from './PathSearch'; -import Settings from './Settings'; -import './Dashboard.css'; - -interface DashboardProps { - connection: VaultConnection; - onLogout: () => void; -} - -function Dashboard({ connection, onLogout }: DashboardProps) { - const [currentPath, setCurrentPath] = useState(''); - const [secretData, setSecretData] = useState | null>(null); - const [isLoading, setIsLoading] = useState(false); - const [showSettings, setShowSettings] = useState(false); - const [showSearch, setShowSearch] = useState(false); - - const handleReadSecret = async (path?: string) => { - const pathToRead = path || currentPath; - - if (!pathToRead) { - alert('Please enter a secret path'); - return; - } - - setIsLoading(true); - setSecretData(null); - - try { - const data = await vaultApi.readSecret( - connection.server, - connection.credentials, - pathToRead - ); - - if (data) { - setSecretData(data); - setCurrentPath(pathToRead); - } else { - alert('Secret not found or empty.'); - } - } catch (error) { - console.error('Error reading secret:', error); - - if (error instanceof VaultError) { - let message = `Failed to read secret: ${error.message}`; - if (error.statusCode) { - message += ` (HTTP ${error.statusCode})`; - } - if (error.errors && error.errors.length > 0) { - message += `\n\nDetails:\n${error.errors.join('\n')}`; - } - - // Special handling for common errors - if (error.statusCode === 403) { - message += '\n\nYou may not have permission to read this secret.'; - } else if (error.statusCode === 404) { - message = 'Secret not found at this path.'; - } else if (error.message.includes('CORS')) { - message += '\n\nCORS error: Make sure your Vault server is configured to allow requests from this origin.'; - } - - alert(message); - } else { - alert('Failed to read secret. Check console for details.'); - } - } finally { - setIsLoading(false); - } - }; - - const handleSelectPath = (path: string) => { - setCurrentPath(path); - handleReadSecret(path); - setShowSearch(false); - }; - - return ( -
-
-
-

Connected to {connection.server.name}

-

{connection.server.url}

-

- Authenticated via {connection.credentials.authMethod} - {connection.lastConnected && ( - - {' '}• Connected at {connection.lastConnected.toLocaleTimeString()} - - )} -

-
-
- - - -
-
- -
- {showSearch && ( - - )} - -
-

Browse Secrets

- -
- -
- setCurrentPath(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && !isLoading && handleReadSecret()} - placeholder="secret/data/myapp/config" - disabled={isLoading} - /> - -
-
- - {secretData && ( -
-

Secret Data

-
-                {JSON.stringify(secretData, null, 2)}
-              
-
- )} - - {!showSearch && ( -
-

Getting Started

-
    -
  • Enter a secret path to read from your Vault server
  • -
  • Example paths: secret/data/myapp/config
  • -
  • Use the Search feature to find secrets recursively
  • -
  • Results are cached to prevent excessive API calls
  • -
-
- )} - -
-

Implementation Notes

-

- This application uses the Vault HTTP API with caching enabled. - The following endpoints are used: -

-
    -
  • List secrets: GET /v1/{'<'}path{'>'}?list=true
  • -
  • Read secret: GET /v1/{'<'}path{'>'}
  • -
  • Write secret: POST/PUT /v1/{'<'}path{'>'}
  • -
  • Delete secret: DELETE /v1/{'<'}path{'>'}
  • -
-

- All requests include the X-Vault-Token header for authentication. - Configure cache settings and search limits in Settings. -

-
-
-
- - {showSettings && ( - setShowSettings(false)} /> - )} -
- ); -} - -export default Dashboard; - diff --git a/src/components/Dashboard.vue b/src/components/Dashboard.vue new file mode 100644 index 0000000..7e8f65d --- /dev/null +++ b/src/components/Dashboard.vue @@ -0,0 +1,211 @@ + + + + diff --git a/src/components/LoginForm.css b/src/components/LoginForm.css deleted file mode 100644 index e8e3e0a..0000000 --- a/src/components/LoginForm.css +++ /dev/null @@ -1,42 +0,0 @@ -.login-form { - height: 100%; -} - -.login-form .section-header { - display: block; - margin-bottom: 1.5rem; -} - -.login-form .section-header h2 { - margin-bottom: 0.5rem; -} - -.login-form .server-url { - color: var(--text-secondary); - font-size: 0.875rem; - font-family: 'Courier New', monospace; -} - -.login-form form { - margin-bottom: 2rem; -} - -.security-notice { - background: var(--surface-light); - border-left: 4px solid var(--primary-color); - padding: 1rem; - border-radius: 4px; - margin-top: 2rem; -} - -.security-notice p { - margin: 0; - font-size: 0.875rem; - color: var(--text-secondary); - line-height: 1.6; -} - -.security-notice strong { - color: var(--text-primary); -} - diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx deleted file mode 100644 index b655e03..0000000 --- a/src/components/LoginForm.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useState } from 'react'; -import { VaultServer, VaultCredentials } from '../types'; -import './LoginForm.css'; - -interface LoginFormProps { - server: VaultServer; - onLogin: (credentials: VaultCredentials) => void; -} - -function LoginForm({ server, onLogin }: LoginFormProps) { - const [authMethod, setAuthMethod] = useState<'token' | 'userpass' | 'ldap'>('token'); - const [token, setToken] = useState(''); - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [isLoading, setIsLoading] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsLoading(true); - - const credentials: VaultCredentials = { - serverId: server.id, - authMethod, - token: authMethod === 'token' ? token : undefined, - username: authMethod !== 'token' ? username : undefined, - password: authMethod !== 'token' ? password : undefined, - }; - - try { - await onLogin(credentials); - } catch (error) { - console.error('Login error:', error); - alert('Login failed. Please check your credentials.'); - } finally { - setIsLoading(false); - } - }; - - return ( -
-
-

Connect to {server.name}

-

{server.url}

-
- -
-
- - -
- - {authMethod === 'token' ? ( -
- - setToken(e.target.value)} - placeholder="Enter your vault token" - required - autoComplete="off" - /> - - Your token will be used to authenticate with the vault server - -
- ) : ( - <> -
- - setUsername(e.target.value)} - placeholder="Enter your username" - required - autoComplete="username" - /> -
- -
- - setPassword(e.target.value)} - placeholder="Enter your password" - required - autoComplete="current-password" - /> -
- - )} - - -
- -
-

- ⚠️ Security Notice: This application connects directly to your - Vault server. Credentials are not stored permanently and are only kept in memory - during your session. -

-
-
- ); -} - -export default LoginForm; - diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue new file mode 100644 index 0000000..1fd5443 --- /dev/null +++ b/src/components/LoginForm.vue @@ -0,0 +1,141 @@ + + + + diff --git a/src/components/PathSearch.css b/src/components/PathSearch.css deleted file mode 100644 index b2e22d9..0000000 --- a/src/components/PathSearch.css +++ /dev/null @@ -1,205 +0,0 @@ -.path-search { - background: var(--surface-light); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 2rem; - border: 1px solid var(--border); -} - -.path-search h3 { - margin: 0 0 1.5rem 0; - font-size: 1.25rem; -} - -.search-controls { - margin-bottom: 1rem; -} - -.search-progress { - display: flex; - align-items: center; - gap: 1rem; - padding: 1.5rem; - background: var(--surface); - border-radius: 6px; - margin: 1rem 0; -} - -.spinner { - width: 24px; - height: 24px; - border: 3px solid var(--border); - border-top-color: var(--primary-color); - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.search-stats { - background: linear-gradient(135deg, rgba(34, 197, 94, 0.1) 0%, rgba(22, 163, 74, 0.1) 100%); - border: 1px solid var(--success-color); - border-radius: 6px; - padding: 1rem; - margin: 1rem 0; -} - -.search-stats p { - margin: 0; - color: var(--text-primary); -} - -.search-results { - margin-top: 1.5rem; -} - -.search-results h4 { - margin: 0 0 1rem 0; - font-size: 1.1rem; -} - -.results-list { - display: flex; - flex-direction: column; - gap: 0.5rem; - max-height: 400px; - overflow-y: auto; - border: 1px solid var(--border); - border-radius: 6px; - padding: 0.5rem; - background: var(--surface); -} - -.result-item { - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.75rem; - background: var(--surface-light); - border-radius: 6px; - border: 1px solid var(--border); - transition: all 0.2s; -} - -.result-item:not(.directory) { - cursor: pointer; -} - -.result-item:not(.directory):hover { - border-color: var(--primary-color); - transform: translateX(4px); -} - -.result-item.directory { - opacity: 0.8; -} - -.result-icon { - font-size: 1.25rem; - flex-shrink: 0; -} - -.result-details { - flex: 1; - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.result-path { - font-family: 'Courier New', monospace; - font-size: 0.9rem; - word-break: break-all; -} - -.result-mount { - font-size: 0.75rem; - color: var(--text-secondary); - font-style: italic; -} - -.result-depth { - font-size: 0.75rem; - color: var(--text-secondary); - padding: 0.25rem 0.5rem; - background: var(--surface); - border-radius: 4px; - flex-shrink: 0; -} - -.result-item .btn { - flex-shrink: 0; -} - -.no-results { - text-align: center; - padding: 2rem; - color: var(--text-secondary); -} - -.no-results p { - margin: 0 0 0.5rem 0; - font-size: 1rem; -} - -.no-results small { - font-size: 0.875rem; - font-style: italic; -} - -.search-info { - background: var(--surface); - border-radius: 6px; - padding: 1rem; - margin-top: 1.5rem; - border: 1px solid var(--border); -} - -.search-info h4 { - margin: 0 0 0.75rem 0; - font-size: 1rem; -} - -.search-info ul { - margin: 0; - padding-left: 1.5rem; -} - -.search-info li { - margin: 0.5rem 0; - font-size: 0.875rem; - color: var(--text-secondary); - line-height: 1.5; -} - -.checkbox-label { - display: flex; - align-items: center; - gap: 0.5rem; - cursor: pointer; -} - -.checkbox-label input[type="checkbox"] { - width: auto; - cursor: pointer; -} - -.checkbox-label input[type="checkbox"]:disabled { - cursor: not-allowed; - opacity: 0.5; -} - -.mount-count { - color: var(--primary-color); - font-weight: 600; -} - -.mount-warning { - color: var(--danger-color); - font-size: 0.875rem; - font-style: italic; -} \ No newline at end of file diff --git a/src/components/PathSearch.tsx b/src/components/PathSearch.tsx deleted file mode 100644 index bef3349..0000000 --- a/src/components/PathSearch.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { useState } from 'react'; -import { VaultServer, VaultCredentials, MountPoint } from '../types'; -import { vaultApi, SearchResult } from '../services/vaultApi'; -import './PathSearch.css'; - -interface PathSearchProps { - server: VaultServer; - credentials: VaultCredentials; - mountPoints?: MountPoint[]; - onSelectPath: (path: string) => void; -} - -function PathSearch({ server, credentials, mountPoints, onSelectPath }: PathSearchProps) { - const [searchTerm, setSearchTerm] = useState(''); - const [basePath, setBasePath] = useState('secret/'); - const [searchAllMounts, setSearchAllMounts] = useState(true); - const [results, setResults] = useState([]); - const [isSearching, setIsSearching] = useState(false); - const [searchTime, setSearchTime] = useState(null); - - // Debug: Log mount points when component mounts or they change - console.log('PathSearch - mountPoints:', mountPoints); - - const handleSearch = async () => { - if (!searchTerm.trim()) { - alert('Please enter a search term'); - return; - } - - if (searchAllMounts && (!mountPoints || mountPoints.length === 0)) { - alert('No mount points available. Please ensure you are connected to Vault.'); - return; - } - - setIsSearching(true); - setResults([]); - setSearchTime(null); - - const startTime = performance.now(); - - try { - let searchResults: SearchResult[]; - - if (searchAllMounts && mountPoints) { - // Search across all mount points - searchResults = await vaultApi.searchAllMounts( - server, - credentials, - mountPoints, - searchTerm - ); - } else { - // Search in specific base path - searchResults = await vaultApi.searchPaths( - server, - credentials, - basePath, - searchTerm - ); - } - - const endTime = performance.now(); - setSearchTime(endTime - startTime); - setResults(searchResults); - } catch (error) { - console.error('Search error:', error); - alert('Search failed. Check console for details.'); - } finally { - setIsSearching(false); - } - }; - - const handleKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !isSearching) { - handleSearch(); - } - }; - - return ( -
-

🔍 Search Paths

- -
-
- - - {!mountPoints || mountPoints.length === 0 ? ( - <>Mount points are detected on login. Please logout and login again to enable this feature. - ) : ( - <>When enabled, searches all KV mount points instead of a specific base path - )} - -
- - {!searchAllMounts && ( -
- - setBasePath(e.target.value)} - placeholder="secret/" - /> - - Starting path for recursive search - -
- )} - -
- -
- setSearchTerm(e.target.value)} - onKeyPress={handleKeyPress} - placeholder="Enter path or keyword..." - disabled={isSearching} - /> - -
-
-
- - {isSearching && ( -
-
-

Searching recursively... This may take a moment.

-
- )} - - {searchTime !== null && ( -
-

- Found {results.length} result{results.length !== 1 ? 's' : ''} - in {(searchTime / 1000).toFixed(2)}s -

-
- )} - - {results.length > 0 && ( -
-

Search Results

-
- {results.map((result, index) => ( -
!result.isDirectory && onSelectPath(result.path)} - > - - {result.isDirectory ? '📁' : '📄'} - -
- {result.path} - {result.mountPoint && searchAllMounts && ( - 📌 {result.mountPoint} - )} -
- Depth: {result.depth} - {!result.isDirectory && ( - - )} -
- ))} -
-
- )} - - {!isSearching && results.length === 0 && searchTime !== null && ( -
-

- No results found for "{searchTerm}" - {searchAllMounts ? ' across all mount points' : ` in ${basePath}`} -

- Try a different search term{!searchAllMounts && ' or base path'} -
- )} - -
-

ℹ️ Search Tips

-
    -
  • Search is case-insensitive and matches partial paths
  • -
  • Results are cached to prevent excessive API calls
  • -
  • - Search all mounts: Enable to search across all KV secret engines - {mountPoints && mountPoints.length > 0 && ( - <> (detected: {mountPoints.map(m => m.path).join(', ')}) - )} -
  • -
  • - Base path: When not searching all mounts, specify a starting path -
  • -
  • Directories are marked with 📁, secrets with 📄
  • -
  • Maximum search depth and results can be configured in settings
  • -
-
-
- ); -} - -export default PathSearch; - diff --git a/src/components/PathSearch.vue b/src/components/PathSearch.vue new file mode 100644 index 0000000..135ea80 --- /dev/null +++ b/src/components/PathSearch.vue @@ -0,0 +1,252 @@ + + + + diff --git a/src/components/ServerSelector.css b/src/components/ServerSelector.css deleted file mode 100644 index 25275a1..0000000 --- a/src/components/ServerSelector.css +++ /dev/null @@ -1,109 +0,0 @@ -.server-selector { - height: 100%; -} - -.section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1.5rem; -} - -.section-header h2 { - margin: 0; -} - -.add-server-form { - background: var(--surface-light); - padding: 1.5rem; - border-radius: 8px; - margin-bottom: 1.5rem; - border: 1px solid var(--border); -} - -.server-list { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.empty-state { - text-align: center; - padding: 3rem 1rem; - color: var(--text-secondary); -} - -.empty-state p { - margin: 0.5rem 0; -} - -.empty-state .hint { - font-size: 0.875rem; - font-style: italic; -} - -.server-card { - background: var(--surface-light); - border: 2px solid var(--border); - border-radius: 8px; - padding: 1.25rem; - cursor: pointer; - transition: all 0.2s; - display: flex; - justify-content: space-between; - align-items: center; -} - -.server-card:hover { - border-color: var(--primary-color); - transform: translateY(-2px); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.server-card.selected { - border-color: var(--primary-color); - background: var(--surface); - box-shadow: 0 0 0 3px rgba(100, 108, 255, 0.1); -} - -.server-info { - flex: 1; -} - -.server-info h3 { - margin: 0 0 0.5rem 0; - font-size: 1.25rem; -} - -.server-url { - color: var(--text-secondary); - font-size: 0.875rem; - margin: 0.25rem 0; - font-family: 'Courier New', monospace; -} - -.server-description { - color: var(--text-secondary); - font-size: 0.875rem; - margin: 0.5rem 0 0 0; - font-style: italic; -} - -.server-kv-version { - margin: 0.5rem 0 0 0; -} - -.badge { - display: inline-block; - padding: 0.25rem 0.5rem; - font-size: 0.75rem; - font-weight: 600; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 4px; - color: var(--text-secondary); -} - -.server-card .btn-danger { - margin-left: 1rem; -} \ No newline at end of file diff --git a/src/components/ServerSelector.tsx b/src/components/ServerSelector.tsx deleted file mode 100644 index a5cbe2a..0000000 --- a/src/components/ServerSelector.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { useState } from 'react'; -import { VaultServer } from '../types'; -import './ServerSelector.css'; - -interface ServerSelectorProps { - servers: VaultServer[]; - selectedServer: VaultServer | null; - onAddServer: (server: VaultServer) => void; - onRemoveServer: (serverId: string) => void; - onSelectServer: (server: VaultServer) => void; -} - -function ServerSelector({ - servers, - selectedServer, - onAddServer, - onRemoveServer, - onSelectServer, -}: ServerSelectorProps) { - const [showAddForm, setShowAddForm] = useState(false); - const [newServer, setNewServer] = useState({ - name: '', - url: '', - description: '', - kvVersion: 2 as 1 | 2, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (!newServer.name || !newServer.url) return; - - const server: VaultServer = { - id: newServer.name, - // id: crypto.randomUUID(), - name: newServer.name, - url: newServer.url, - description: newServer.description || undefined, - kvVersion: newServer.kvVersion, - }; - - onAddServer(server); - setNewServer({ name: '', url: '', description: '', kvVersion: 2 }); - setShowAddForm(false); - }; - - return ( -
-
-

Vault Servers

- -
- - {showAddForm && ( -
-
- - setNewServer({ ...newServer, name: e.target.value })} - placeholder="Production Vault" - required - /> -
- -
- - setNewServer({ ...newServer, url: e.target.value })} - placeholder="https://vault.example.com" - required - /> -
- -
- - setNewServer({ ...newServer, description: e.target.value })} - placeholder="Optional description" - /> -
- -
- - - - Most Vault servers use KV v2. Choose v1 only for legacy installations. - -
- - -
- )} - -
- {servers.length === 0 ? ( -
-

No vault servers configured yet.

-

Click "Add Server" to get started.

-
- ) : ( - servers.map((server) => ( -
onSelectServer(server)} - > -
-

{server.name}

-

{server.url}

- {server.description && ( -

{server.description}

- )} -

- KV v{server.kvVersion || 2} -

-
- -
- )) - )} -
-
- ); -} - -export default ServerSelector; - diff --git a/src/components/ServerSelector.vue b/src/components/ServerSelector.vue new file mode 100644 index 0000000..efcb2b6 --- /dev/null +++ b/src/components/ServerSelector.vue @@ -0,0 +1,162 @@ + + + + diff --git a/src/components/Settings.css b/src/components/Settings.css deleted file mode 100644 index 036441e..0000000 --- a/src/components/Settings.css +++ /dev/null @@ -1,151 +0,0 @@ -.settings-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - padding: 1rem; -} - -.settings-modal { - background: var(--surface); - border-radius: 12px; - max-width: 700px; - width: 100%; - max-height: 90vh; - overflow-y: auto; - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3); - border: 1px solid var(--border); -} - -.settings-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1.5rem; - border-bottom: 1px solid var(--border); -} - -.settings-header h2 { - margin: 0; - font-size: 1.5rem; -} - -.btn-close { - background: none; - border: none; - font-size: 2rem; - line-height: 1; - cursor: pointer; - padding: 0; - width: 40px; - height: 40px; - border-radius: 6px; - color: var(--text-secondary); - transition: all 0.2s; -} - -.btn-close:hover { - background: var(--surface-light); - color: var(--text-primary); -} - -.settings-content { - padding: 1.5rem; -} - -.settings-section { - margin-bottom: 2rem; -} - -.settings-section:last-child { - margin-bottom: 0; -} - -.settings-section h3 { - margin: 0 0 1rem 0; - font-size: 1.25rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid var(--border); -} - -.settings-section .form-group label[for] { - cursor: pointer; -} - -.settings-section input[type="checkbox"] { - width: auto; - margin-right: 0.5rem; - cursor: pointer; -} - -.cache-stats { - background: var(--surface-light); - border-radius: 6px; - padding: 1rem; - margin-top: 1.5rem; - border: 1px solid var(--border); -} - -.cache-stats h4 { - margin: 0 0 1rem 0; - font-size: 1rem; -} - -.cache-stats dl { - display: grid; - grid-template-columns: auto 1fr; - gap: 0.5rem 1rem; - margin: 0 0 1rem 0; -} - -.cache-stats dt { - font-weight: 500; - color: var(--text-secondary); -} - -.cache-stats dd { - margin: 0; - font-family: 'Courier New', monospace; - color: var(--text-primary); -} - -.settings-footer { - display: flex; - justify-content: flex-end; - gap: 1rem; - padding: 1.5rem; - border-top: 1px solid var(--border); -} - -.btn-secondary { - background-color: var(--surface-light); - color: var(--text-primary); - border: 1px solid var(--border); -} - -.btn-secondary:hover { - background-color: var(--border); -} - -@media (max-width: 768px) { - .settings-modal { - max-height: 100vh; - border-radius: 0; - } - - .cache-stats dl { - grid-template-columns: 1fr; - gap: 0.25rem; - } - - .cache-stats dt { - font-weight: 600; - } -} - diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx deleted file mode 100644 index d2df1a3..0000000 --- a/src/components/Settings.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import { useState, useEffect } from 'react'; -import { AppConfig, loadConfig, saveConfig } from '../config'; -import { vaultCache } from '../utils/cache'; -import './Settings.css'; - -interface SettingsProps { - onClose: () => void; -} - -function Settings({ onClose }: SettingsProps) { - const [config, setConfig] = useState(loadConfig()); - const [cacheStats, setCacheStats] = useState(vaultCache.getStats()); - - useEffect(() => { - // Update cache stats - const interval = setInterval(() => { - setCacheStats(vaultCache.getStats()); - }, 1000); - - return () => clearInterval(interval); - }, []); - - const handleSave = () => { - saveConfig(config); - alert('Settings saved successfully!'); - onClose(); - }; - - const handleClearCache = () => { - if (confirm('Are you sure you want to clear the cache?')) { - vaultCache.clear(); - setCacheStats(vaultCache.getStats()); - alert('Cache cleared successfully!'); - } - }; - - const formatBytes = (bytes: number): string => { - if (bytes === 0) return '0 Bytes'; - const k = 1024; - const sizes = ['Bytes', 'KB', 'MB', 'GB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; - }; - - const formatDate = (timestamp: number | null): string => { - if (!timestamp) return 'N/A'; - return new Date(timestamp).toLocaleString(); - }; - - return ( -
-
e.stopPropagation()}> -
-

⚙️ Settings

- -
- -
-
-

Cache Settings

- -
- - - Cache API responses to reduce load on Vault server - -
- -
- - setConfig({ - ...config, - cache: { ...config.cache, maxSizeMB: parseInt(e.target.value) || 10 } - })} - /> - - Maximum size of cached data in megabytes - -
- -
- - setConfig({ - ...config, - cache: { ...config.cache, maxAge: (parseInt(e.target.value) || 30) * 60 * 1000 } - })} - /> - - How long cached entries remain valid - -
- -
-

Cache Statistics

-
-
Total Size:
-
{formatBytes(cacheStats.totalSize)}
- -
Entry Count:
-
{cacheStats.entryCount}
- -
Oldest Entry:
-
{formatDate(cacheStats.oldestEntry)}
- -
Newest Entry:
-
{formatDate(cacheStats.newestEntry)}
-
- -
-
- -
-

Search Settings

- -
- - setConfig({ - ...config, - search: { ...config.search, maxDepth: parseInt(e.target.value) || 10 } - })} - /> - - Maximum recursion depth for path searches - -
- -
- - setConfig({ - ...config, - search: { ...config.search, maxResults: parseInt(e.target.value) || 1000 } - })} - /> - - Maximum number of results to return from a search - -
-
-
- -
- - -
-
-
- ); -} - -export default Settings; - diff --git a/src/components/Settings.vue b/src/components/Settings.vue new file mode 100644 index 0000000..6de2159 --- /dev/null +++ b/src/components/Settings.vue @@ -0,0 +1,217 @@ + + + + diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..230001c --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,8 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} + diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 9981ba4..0000000 --- a/src/index.css +++ /dev/null @@ -1,133 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - --primary-color: #646cff; - --primary-hover: #535bf2; - --success-color: #22c55e; - --success-hover: #16a34a; - --danger-color: #ef4444; - --danger-hover: #dc2626; - --background: #242424; - --surface: #1a1a1a; - --surface-light: #2d2d2d; - --border: #3d3d3d; - --text-primary: rgba(255, 255, 255, 0.87); - --text-secondary: rgba(255, 255, 255, 0.6); -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - --primary-color: #646cff; - --primary-hover: #535bf2; - --background: #ffffff; - --surface: #f9fafb; - --surface-light: #f3f4f6; - --border: #e5e7eb; - --text-primary: #213547; - --text-secondary: #6b7280; - } -} - -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; - background: var(--background); -} - -#root { - width: 100%; - min-height: 100vh; -} - -h1, h2, h3, h4, h5, h6 { - line-height: 1.2; - color: var(--text-primary); -} - -a { - font-weight: 500; - color: var(--primary-color); - text-decoration: inherit; -} - -a:hover { - color: var(--primary-hover); -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - cursor: pointer; - transition: all 0.25s; -} - -button:hover { - border-color: var(--primary-color); -} - -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -input, -select, -textarea { - font-family: inherit; - font-size: 1em; - padding: 0.6em; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--surface); - color: var(--text-primary); - transition: border-color 0.25s; -} - -input:focus, -select:focus, -textarea:focus { - outline: none; - border-color: var(--primary-color); -} - -code { - background-color: var(--surface-light); - padding: 2px 6px; - border-radius: 4px; - font-family: 'Courier New', monospace; -} - -pre { - background-color: var(--surface); - padding: 1em; - border-radius: 8px; - overflow-x: auto; - border: 1px solid var(--border); -} - diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..216546d --- /dev/null +++ b/src/main.ts @@ -0,0 +1,6 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') + diff --git a/src/main.tsx b/src/main.tsx deleted file mode 100644 index 9aa0f48..0000000 --- a/src/main.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' -import './index.css' - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -) - diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..23b87cc --- /dev/null +++ b/src/style.css @@ -0,0 +1,30 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Custom scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + @apply bg-base-300; +} + +::-webkit-scrollbar-thumb { + @apply bg-base-content/30 rounded; +} + +::-webkit-scrollbar-thumb:hover { + @apply bg-base-content/50; +} + +/* Code blocks */ +pre { + @apply bg-base-300 p-4 rounded-lg overflow-x-auto text-sm; +} + +code { + @apply bg-base-300 px-2 py-1 rounded text-sm font-mono; +} \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts deleted file mode 100644 index ed77210..0000000 --- a/src/vite-env.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// - diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..bccfec8 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,16 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [require("daisyui")], + daisyui: { + themes: ["dark", "light"], + darkTheme: "dark", + }, +} + diff --git a/tsconfig.json b/tsconfig.json index 256e8d8..e21d62f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,25 +2,37 @@ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], "module": "ESNext", "skipLibCheck": true, - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx", - + "jsx": "preserve", + /* Vue 3 specific */ + "types": [ + "vite/client" + ], /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] -} - + "include": [ + "src/**/*.ts", + "src/**/*.vue" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 962333c..37e3700 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,8 +1,8 @@ import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [vue()], })