commit 5bf668e02931f982b04d33222e05bb567deaac97 Author: koplenov Date: Sat Apr 4 21:23:18 2026 +0300 bump diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..7dbf7eb --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/index.html b/index.html new file mode 100644 index 0000000..5b9ae48 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + my-react-app + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..602a3a2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1000 @@ +{ + "name": "my-react-app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "my-react-app", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "typescript": "~5.9.3", + "vite": "^8.0.1" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..44354e9 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "my-react-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "typescript": "~5.9.3", + "vite": "^8.0.1" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/windows_10.jpg b/public/images/windows_10.jpg new file mode 100644 index 0000000..26662af Binary files /dev/null and b/public/images/windows_10.jpg differ diff --git a/public/images/windows_7.jpg b/public/images/windows_7.jpg new file mode 100644 index 0000000..cddfebb Binary files /dev/null and b/public/images/windows_7.jpg differ diff --git a/public/images/windows_xp.jpg b/public/images/windows_xp.jpg new file mode 100644 index 0000000..214eb6b Binary files /dev/null and b/public/images/windows_xp.jpg differ diff --git a/public/recorderWorker.js b/public/recorderWorker.js new file mode 100644 index 0000000..2e99f5c --- /dev/null +++ b/public/recorderWorker.js @@ -0,0 +1,116 @@ +/* Recorder.js worker — https://github.com/mattdiamond/Recorderjs */ +var recLength = 0, + recBuffers = [], + sampleRate, + numChannels; + +this.onmessage = function(e) { + switch (e.data.command) { + case 'init': init(e.data.config); break; + case 'record': record(e.data.buffer); break; + case 'exportWAV': exportWAV(e.data.type); break; + case 'getBuffer': getBuffer(); break; + case 'clear': clear(); break; + } +}; + +function init(config) { + sampleRate = config.sampleRate; + numChannels = config.numChannels; + initBuffers(); +} + +function record(inputBuffer) { + for (var channel = 0; channel < numChannels; channel++) { + recBuffers[channel].push(inputBuffer[channel]); + } + recLength += inputBuffer[0].length; +} + +function exportWAV(type) { + var buffers = []; + for (var channel = 0; channel < numChannels; channel++) { + buffers.push(mergeBuffers(recBuffers[channel], recLength)); + } + var interleaved = numChannels === 2 + ? interleave(buffers[0], buffers[1]) + : buffers[0]; + var dataview = encodeWAV(interleaved); + var audioBlob = new Blob([dataview], { type: type }); + this.postMessage({ command: 'exportWAV', data: audioBlob }); +} + +function getBuffer() { + var buffers = []; + for (var channel = 0; channel < numChannels; channel++) { + buffers.push(mergeBuffers(recBuffers[channel], recLength)); + } + this.postMessage({ command: 'getBuffer', data: buffers }); +} + +function clear() { + recLength = 0; + recBuffers = []; + initBuffers(); +} + +function initBuffers() { + for (var channel = 0; channel < numChannels; channel++) { + recBuffers[channel] = []; + } +} + +function mergeBuffers(recBuffers, recLength) { + var result = new Float32Array(recLength); + var offset = 0; + for (var i = 0; i < recBuffers.length; i++) { + result.set(recBuffers[i], offset); + offset += recBuffers[i].length; + } + return result; +} + +function interleave(inputL, inputR) { + var length = inputL.length + inputR.length; + var result = new Float32Array(length); + var index = 0, inputIndex = 0; + while (inputIndex < inputL.length) { + result[index++] = inputL[inputIndex]; + result[index++] = inputR[inputIndex]; + inputIndex++; + } + return result; +} + +function floatTo16BitPCM(output, offset, input) { + for (var i = 0; i < input.length; i++, offset += 2) { + var s = Math.max(-1, Math.min(1, input[i])); + output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); + } +} + +function writeString(view, offset, string) { + for (var i = 0; i < string.length; i++) { + view.setUint8(offset + i, string.charCodeAt(i)); + } +} + +function encodeWAV(samples) { + var buffer = new ArrayBuffer(44 + samples.length * 2); + var view = new DataView(buffer); + writeString(view, 0, 'RIFF'); + view.setUint32(4, 36 + samples.length * 2, true); + writeString(view, 8, 'WAVE'); + writeString(view, 12, 'fmt '); + view.setUint32(16, 16, true); + view.setUint16(20, 1, true); + view.setUint16(22, numChannels, true); + view.setUint32(24, sampleRate, true); + view.setUint32(28, sampleRate * numChannels * 2, true); + view.setUint16(32, numChannels * 2, true); + view.setUint16(34, 16, true); + writeString(view, 36, 'data'); + view.setUint32(40, samples.length * 2, true); + floatTo16BitPCM(view, 44, samples); + return view; +} diff --git a/public/recorderWorklet.js b/public/recorderWorklet.js new file mode 100644 index 0000000..8bf2ab0 --- /dev/null +++ b/public/recorderWorklet.js @@ -0,0 +1,28 @@ +/** + * AudioWorkletProcessor для записи PCM-данных. + * Отправляет Float32Array-чанки на главный поток через port. + */ +class RecorderProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this._active = true; + this.port.onmessage = (e) => { + if (e.data === 'stop') this._active = false; + }; + } + + process(inputs) { + if (!this._active) return false; // выгружаем процессор + + const input = inputs[0]; + if (input && input.length > 0) { + // Копируем каналы — данные валидны только внутри process() + const channels = input.map(ch => new Float32Array(ch)); + // Transferable — избегаем лишних копий + this.port.postMessage(channels, channels.map(c => c.buffer)); + } + return true; + } +} + +registerProcessor('recorder-processor', RecorderProcessor); diff --git a/public/yandex_search.jpg b/public/yandex_search.jpg new file mode 100644 index 0000000..22bbe79 Binary files /dev/null and b/public/yandex_search.jpg differ diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..6072fea --- /dev/null +++ b/src/App.css @@ -0,0 +1,267 @@ + +.day2-desktop { + background-color: #1a1a1a; +} + +.quest-panel { + position: fixed; + top: 0; + left: 0; + right: 0; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + padding: 15px; + llotext-align: center; + z-index: 1000; + box-shadow: 0 2px 10px rgba(0,0,0,0.2); +} + +.quest-panel.day2 { + background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); +} + +.quest-header { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 1200px; + margin: 0 auto; + color: white; + font-weight: bold; +} + +.quest-day { + font-size: 18px; +} + +.quest-task { + background: rgba(255,255,255,0.2); + padding: 5px 15px; + border-radius: 20px; + font-size: 14px; +} + +.update-panel-day1 { + position: fixed; + top: 80px; + right: 20px; + background: rgba(0, 0, 0, 0.85); + backdrop-filter: blur(10px); + padding: 20px; + border-radius: 15px; + color: white; + z-index: 1000; + min-width: 280px; + box-shadow: 0 4px 20px rgba(0,0,0,0.3); + border: 1px solid rgba(255,255,255,0.2); +} + +.system-info { + display: flex; + align-items: center; + gap: 15px; + margin-bottom: 15px; +} + +.version-icon { + font-size: 32px; +} + +.version-details strong { + display: block; + margin-bottom: 5px; +} + +.update-status { + font-size: 12px; + color: #4caf50; + margin-top: 5px; +} + +.panel-buttons { + display: flex; + gap: 10px; + margin-top: 15px; +} + +.update-btn, .end-day-btn { + flex: 1; + padding: 10px; + border: none; + border-radius: 8px; + cursor: pointer; + font-weight: bold; + transition: all 0.3s; +} + +.update-btn { + background: #4caf50; + color: white; +} + +.update-btn:hover { + background: #45a049; + transform: scale(1.02); +} + +.end-day-btn { + background: #ff9800; + color: white; +} + +.end-day-btn:hover { + background: #f57c00; + transform: scale(1.02); +} + +.update-message { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0,0,0,0.9); + color: white; + padding: 20px 40px; + border-radius: 15px; + font-size: 20px; + font-weight: bold; + z-index: 2000; + animation: fadeInOut 2s ease-in-out; + white-space: nowrap; +} + +@keyframes fadeInOut { + 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } + 15% { opacity: 1; transform: translate(-50%, -50%) scale(1); } + 85% { opacity: 1; transform: translate(-50%, -50%) scale(1); } + 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } +} + +.day1-decoration { + position: fixed; + bottom: 20px; + left: 0; + right: 0; + text-align: center; + pointer-events: none; +} + +.watermark { + color: rgba(255,255,255,0.3); + font-size: 14px; + padding: 10px; + background: rgba(0,0,0,0.3); + display: inline-block; + border-radius: 20px; +} + +.taskbar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 50px; + background: rgba(0, 0, 0, 0.85); + backdrop-filter: blur(10px); + display: flex; + align-items: center; + padding: 0 10px; + z-index: 1000; + color: white; + border-top: 1px solid rgba(255,255,255,0.2); +} + +.start-button { + background: linear-gradient(135deg, #4caf50, #45a049); + padding: 8px 20px; + border-radius: 5px; + cursor: pointer; + font-weight: bold; + margin-right: 20px; + transition: all 0.3s; +} + +.start-button:hover { + transform: scale(1.05); + box-shadow: 0 2px 10px rgba(0,0,0,0.3); +} + +.taskbar-icons { + flex: 1; + display: flex; + gap: 5px; + overflow-x: auto; +} + +.taskbar-item { + padding: 8px 15px; + background: rgba(255,255,255,0.1); + border-radius: 5px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.3s; + white-space: nowrap; +} + +.taskbar-item:hover { + background: rgba(255,255,255,0.2); +} + +.taskbar-item.minimized { + opacity: 0.6; +} + +.system-tray { + display: flex; + align-items: center; + gap: 15px; + margin-left: 20px; +} + +.version-badge { + background: rgba(0,0,0,0.5); + padding: 5px 10px; + border-radius: 5px; + font-size: 12px; +} + +.clock { + font-family: monospace; + font-size: 14px; + background: rgba(0,0,0,0.5); + padding: 5px 10px; + border-radius: 5px; +} + + +.icons-container { + position: absolute; + top: 80px; + left: 20px; + display: flex; + flex-direction: column; + gap: 20px; + z-index: 10; +} + +.quest-panel.day2 { + background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); +} + +.icons-container { + position: absolute; + top: 80px; + left: 20px; + display: flex; + flex-direction: column; + gap: 20px; + z-index: 10; +} + +.desktop { + width: 100vw; + height: 100vh; + overflow: hidden; + position: relative; +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..15b675b --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,18 @@ +import React, { useState } from 'react'; +// import Day1Desktop from './Day1Desktop'; +import MainApp from './MainApp'; +import ChatterboxTTS from './components/deepfake/ChatterboxTTS'; + +export type WallpaperType = 'xp' | 'win7' | 'win10'; + +const App: React.FC = () => { +// const [day1Complete, setDay1Complete] = useState(false); + +// if (!day1Complete) { + // return setDay1Complete(true)} />; +// } + return + return ; +}; + +export default App; diff --git a/src/Day1Desktop.css b/src/Day1Desktop.css new file mode 100644 index 0000000..2818adf --- /dev/null +++ b/src/Day1Desktop.css @@ -0,0 +1,126 @@ +.day1-container { + width: 100%; + height: 100%; + font-family: "Raleway", sans-serif; +} + +.day1-wallpaper { + width: 100%; + height:100%; + object-fit: cover; +} + +.day1-header { + position: absolute; + top: 0; + left: 0; + right: 0; + font-family: "Raleway", sans-serif; + background: rgba(47, 37, 191, 0.4); + padding: 15px; + color: white; + text-align: center; +} + +.day1-header-content { + display: flex; + font-family: Arial, Helvetica, sans-serif; + justify-content: space-around; +} + +.day1-panel { + position: absolute; + bottom: 100px; + left: 50%; + transform: translateX(-50%); + background: rgba(47, 37, 191, 0.4); + padding: 20px; + border-radius: 10px; + color: white; + text-align: center; + min-width: 300px; +} + +.day1-version-info { + margin-bottom: 15px; +} + +.day1-buttons { + display: flex; + gap: 10px; + justify-content: center; +} + +.day1-update-btn { + padding: 10px 20px; + background: #2a0c84; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.day1-update-btn:hover:not(:disabled) { + background: #4313a3; +} + +.day1-update-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.day1-end-btn { + padding: 10px 20px; + background: #e0af0f; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.day1-end-btn:hover { + background: #f38518; +} + +.day1-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 2000; +} + +.day1-modal { + background: white; + padding: 20px; + border-radius: 10px; + text-align: center; + max-width: 400px; +} + +.day1-modal-title { + color: #f44336; + margin-bottom: 10px; +} + +.day1-modal-text { + margin-bottom: 20px; +} + +.day1-modal-btn { + padding: 10px 20px; + background: #4caf50; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.day1-modal-btn:hover { + background: #45a049; +} \ No newline at end of file diff --git a/src/Day1Desktop.tsx b/src/Day1Desktop.tsx new file mode 100644 index 0000000..40c65c8 --- /dev/null +++ b/src/Day1Desktop.tsx @@ -0,0 +1,100 @@ +import React, { useState } from 'react'; +import type { WallpaperType } from './App'; +import './Day1Desktop.css'; + +interface Day1DesktopProps { + onComplete: (type: WallpaperType) => void; +} + +const Day1Desktop: React.FC = ({ onComplete }) => { + const [version, setVersion] = useState(1); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const getImage = () => { + if (version === 1) return '/images/windows_xp.jpg'; + if (version === 2) return '/images/windows_7.jpg'; + return '/images/windows_10.jpg'; + }; + + const getVersionName = () => { + if (version === 1) return 'Windows XP'; + if (version === 2) return 'Windows 7'; + return 'Windows 10'; + }; + + const getWallpaperType = (): WallpaperType => { + if (version === 1) return 'xp'; + if (version === 2) return 'win7'; + return 'win10'; + }; + + const updateSystem = () => { + if (version === 1) { + setLoading(true); + setTimeout(() => { + setVersion(2); + setLoading(false); + }, 2000); + } else if (version === 2) { + setLoading(true); + setTimeout(() => { + setVersion(3); + setLoading(false); + }, 2000); + } + }; + + const endDay = () => { + if (version === 3) { + onComplete(getWallpaperType()); + } else { + setError(`Ошибка! Нельзя завершить день на старой версии ${getVersionName()}. Нужно обновиться до Windows 10`); + } + }; + + return ( +
+ wallpaper + +
+
+ Кейс 1: Обновление системы + Выберите наиболее подходящую версию Windows +
+
+ +
+
+ Текущая версия: {getVersionName()} +
+
+ {version !== 3 && ( + + )} + +
+
+ + {error && ( +
+
+

Ошибка

+

{error}

+ +
+
+ )} +
+ ); +}; + +export default Day1Desktop; diff --git a/src/MainApp.tsx b/src/MainApp.tsx new file mode 100644 index 0000000..05c0c49 --- /dev/null +++ b/src/MainApp.tsx @@ -0,0 +1,98 @@ +import React, { useState } from 'react'; +import DesktopIcon from './components/DesktopIcon'; +import Window from './components/Window'; +import Sidebar from './components/Sidebar'; +import './App.css'; +import VScodeApp from './apps/vscode/vscode'; +import YandexApp from './apps/yandex/Yandex'; +import TerminalApp from './apps/terminal/Terminal'; + +export interface WindowType { + id: string; + title: string; + content: any; + icon: string; + isMinimized: boolean; + url: string; +} + +const MainApp: React.FC = () => { + const [windows, setWindows] = useState([]); + const [nextId, setNextId] = useState(1); + + const openWindow = (title: string, content: any, icon: string, url: string) => { + const newWindow: WindowType = { + id: `window-${nextId}`, + title, + content, + icon, + isMinimized: false, + url, + }; + setWindows([...windows, newWindow]); + setNextId(nextId + 1); + }; + + const closeWindow = (id: string) => { + setWindows(windows.filter(w => w.id !== id)); + }; + + const minimizeWindow = (id: string) => { + setWindows(windows.map(w => + w.id === id ? { ...w, isMinimized: !w.isMinimized } : w + )); + }; + + const restoreWindow = (id: string) => { + setWindows(windows.map(w => + w.id === id ? { ...w, isMinimized: false } : w + )); + }; + + const openVS = () => { + openWindow('VS Code', , '💻', 'https://code.visualstudio.com'); + }; + + const openYandex = () => { + openWindow('Yandex', , '🌐', 'https://yandex.ru'); + }; + + const openTerminal = () => { + openWindow('Terminal', , 'T', 'https://oop.com'); + }; + + return ( +
+
+ + + +
+ + {windows.map(window => !window.isMinimized && ( + + ))} + + +
+ ); +}; + +export default MainApp; \ No newline at end of file diff --git a/src/apps/terminal/Terminal.tsx b/src/apps/terminal/Terminal.tsx new file mode 100644 index 0000000..eddb95f --- /dev/null +++ b/src/apps/terminal/Terminal.tsx @@ -0,0 +1,101 @@ +import { useState, useRef, useEffect } from 'react'; +import type { KeyboardEvent } from 'react'; + +const TerminalApp = () => { + const [currentTaskState, setTaskState] = useState(false); + const [is_updated_libraries_state, set_updated_libraries_state] = useState(false); + + const [input, setInput] = useState(''); + const [history, setHistory] = useState([ + { type: 'system', text: 'Добро пожаловать в мини-терминал. Введите "help" для списка команд.' } + ]); + const bottomRef = useRef(null); + + // Автопрокрутка вниз при новых сообщениях + useEffect(() => { + bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [history]); + + const commands = { + help: () => 'Доступные команды: help, clear, apt', + + "apt": () => "apt is package manager. Use 'apt help' for more info", + "apt help": () => "apt avalible commands: apt update; apt upgrade;", + "apt update": () => { + set_updated_libraries_state(true); + return "Yup, package list update, you can update system" + }, + "apt upgrade": () => { + if(is_updated_libraries_state) { + setTaskState(true) // mark as done + alert("Done!") + } + return "Yup, system and libraries updated for latest version" + }, + + clear: () => { + setHistory([]); + return null; + } + }; + + const handleCommand = (e: KeyboardEvent) => { + if (e.key === 'Enter') { + const cleanInput = input.trim().toLowerCase(); + const newHistory = [...history, { type: 'user', text: `> ${input}` }]; + + if (cleanInput) { + if (cleanInput in commands) { + const result = commands[cleanInput as keyof typeof commands](); + if (result){ + newHistory.push({ type: 'bot', text: result }); + setHistory(newHistory); + } + } else { + newHistory.push({ type: 'error', text: `Команда "${cleanInput}" не найдена. Введите "help".` }); + setHistory(newHistory); + } + } + + setInput(''); + } + }; + + return ( +
+
+ {history.map((line, i) => ( +
+ {line.text} +
+ ))} +
+
+
+ $ + setInput(e.target.value)} + onKeyDown={handleCommand} + autoFocus + /> +
+ {currentTaskState} +
+ ); +}; + +const styles = { + container: { backgroundColor: '#1e1e1e', color: '#00ff00', padding: '20px', fontFamily: 'monospace', height: '400px', display: 'flex', flexDirection: 'column' as const, borderRadius: '8px' }, + terminal: { flex: 1, overflowY: 'auto' as const, marginBottom: '10px' }, + line: { marginBottom: '4px', whiteSpace: 'pre-wrap' as const, textAlign: 'justify' as const }, + user: { color: '#fff' }, + error: { color: '#ff5555' }, + system: { color: '#aaa' }, + inputLine: { display: 'flex', alignItems: 'center' }, + prompt: { marginRight: '8px', fontWeight: 'bold' }, + input: { background: 'transparent', border: 'none', color: '#00ff00', outline: 'none', flex: 1, fontFamily: 'monospace', fontSize: '16px' } +}; + +export default TerminalApp; diff --git a/src/apps/vscode/vscode.css b/src/apps/vscode/vscode.css new file mode 100644 index 0000000..21f0f7b --- /dev/null +++ b/src/apps/vscode/vscode.css @@ -0,0 +1,3 @@ +.vscode_main_window { + background: blue; +} \ No newline at end of file diff --git a/src/apps/vscode/vscode.tsx b/src/apps/vscode/vscode.tsx new file mode 100644 index 0000000..e351194 --- /dev/null +++ b/src/apps/vscode/vscode.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import './vscode.css'; + +const VScodeApp: React.FC = () => { + return ( +
+

Vscode

+ + +
+ ); +}; + +export default VScodeApp; \ No newline at end of file diff --git a/src/apps/yandex/Yandex.css b/src/apps/yandex/Yandex.css new file mode 100644 index 0000000..e11efdd --- /dev/null +++ b/src/apps/yandex/Yandex.css @@ -0,0 +1,5 @@ +.yandex { + width: 100%; + height: 100%; + background: url("/yandex_search.jpg"); +} \ No newline at end of file diff --git a/src/apps/yandex/Yandex.tsx b/src/apps/yandex/Yandex.tsx new file mode 100644 index 0000000..e19f119 --- /dev/null +++ b/src/apps/yandex/Yandex.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import './Yandex.css'; + +const YandexApp: React.FC = () => { + return ( +
+ {/*

Vscode

+ + */} +
+ ); +}; + +export default YandexApp; \ No newline at end of file diff --git a/src/assets/hero.png b/src/assets/hero.png new file mode 100644 index 0000000..cc51a3d Binary files /dev/null and b/src/assets/hero.png differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/vite.svg b/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/src/components/DekstopIcon.css b/src/components/DekstopIcon.css new file mode 100644 index 0000000..a355606 --- /dev/null +++ b/src/components/DekstopIcon.css @@ -0,0 +1,29 @@ +.desktop-icon { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 80px; + padding: 10px; + cursor: pointer; + border-radius: 8px; + transition: all 0.2s; + color: white; + text-shadow: 0 1px 2px rgba(0,0,0,0.5); +} + +.desktop-icon:hover { + background: rgba(255,255,255,0.2); + transform: scale(1.05); +} + +.icon-image { + font-size: 48px; + margin-bottom: 8px; +} + +.icon-name { + font-size: 12px; + text-align: center; + font-weight: 500; +} \ No newline at end of file diff --git a/src/components/DesktopIcon.tsx b/src/components/DesktopIcon.tsx new file mode 100644 index 0000000..0f6b098 --- /dev/null +++ b/src/components/DesktopIcon.tsx @@ -0,0 +1,54 @@ +import React from 'react'; + +interface DesktopIconProps { + icon: string; + name: string; + onClick: () => void; +} + +const DesktopIcon: React.FC = ({ icon, name, onClick }) => { + const styles = { + desktopIcon: { + display: 'flex' as const, + flexDirection: 'column' as const, + alignItems: 'center', + justifyContent: 'center', + width: '80px', + padding: '10px', + cursor: 'pointer', + borderRadius: '8px', + transition: 'all 0.2s', + color: 'white', + textShadow: '0 1px 2px rgba(0,0,0,0.5)', + }, + iconImage: { + fontSize: '48px', + marginBottom: '8px', + }, + iconName: { + fontSize: '12px', + textAlign: 'center' as const, + fontWeight: 500, + }, + }; + + return ( +
{ + e.currentTarget.style.background = 'rgba(255,255,255,0.2)'; + e.currentTarget.style.transform = 'scale(1.05)'; + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = 'transparent'; + e.currentTarget.style.transform = 'scale(1)'; + }} + > +
{icon}
+ {name} +
+ ); +}; + +export default DesktopIcon; \ No newline at end of file diff --git a/src/components/Sidebar.css b/src/components/Sidebar.css new file mode 100644 index 0000000..15bb794 --- /dev/null +++ b/src/components/Sidebar.css @@ -0,0 +1,66 @@ +.sidebar-bottom { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: rgba(32, 32, 32, 0.95); + backdrop-filter: blur(10px); + display: flex; + gap: 4px; + padding: 8px 16px; + z-index: 1000; + border-top: 1px solid rgba(255,255,255,0.2); + overflow-x: auto; +} + +.sidebar-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + background: rgba(255,255,255,0.1); + border-radius: 8px; + cursor: pointer; + transition: all 0.2s; + color: white; + font-size: 13px; + min-width: 120px; + justify-content: space-between; +} + +.sidebar-item:hover { + background: rgba(255,255,255,0.2); + transform: translateY(-2px); +} + +.sidebar-item.minimized { + background: rgba(255,255,255,0.05); + opacity: 0.7; +} + +.sidebar-icon { + font-size: 16px; +} + +.sidebar-title { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebar-close { + background: transparent; + border: none; + color: white; + cursor: pointer; + font-size: 14px; + padding: 2px 6px; + border-radius: 4px; + transition: all 0.2s; +} + +.sidebar-close:hover { + background: rgba(255,255,255,0.2); + color: #ff4444; +} \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx new file mode 100644 index 0000000..addd8e1 --- /dev/null +++ b/src/components/Sidebar.tsx @@ -0,0 +1,125 @@ +import React from 'react'; + +interface WindowData { + id: string; + title: string; + content: string; + icon: string; + isMinimized: boolean; + url: string; +} + +interface SidebarProps { + windows: WindowData[]; + onRestore: (id: string) => void; + onClose: (id: string) => void; +} + +const Sidebar: React.FC = ({ windows, onRestore, onClose }) => { + const styles = { + sidebar: { + position: 'fixed' as const, + bottom: 0, + left: 0, + right: 0, + background: 'rgba(32, 32, 32, 0.95)', + backdropFilter: 'blur(10px)', + display: 'flex', + gap: '4px', + padding: '8px 16px', + zIndex: 1000, + borderTop: '1px solid rgba(255,255,255,0.2)', + overflowX: 'auto' as const, + }, + item: { + display: 'flex', + alignItems: 'center', + gap: '8px', + padding: '6px 12px', + background: 'rgba(255,255,255,0.1)', + borderRadius: '8px', + cursor: 'pointer', + transition: 'all 0.2s', + color: 'white', + fontSize: '13px', + minWidth: '120px', + justifyContent: 'space-between' as const, + }, + itemMinimized: { + background: 'rgba(255,255,255,0.05)', + opacity: 0.7, + }, + icon: { + fontSize: '16px', + }, + title: { + flex: 1, + whiteSpace: 'nowrap' as const, + overflow: 'hidden' as const, + textOverflow: 'ellipsis', + }, + closeBtn: { + background: 'transparent', + border: 'none', + color: 'white', + cursor: 'pointer', + fontSize: '14px', + padding: '2px 6px', + borderRadius: '4px', + transition: 'all 0.2s', + }, + }; + + if (windows.length === 0) return null; + + return ( +
+ {windows.map((window) => ( +
{ + if (window.isMinimized) { + onRestore(window.id); + } + }} + onMouseEnter={(e) => { + e.currentTarget.style.background = 'rgba(255,255,255,0.2)'; + e.currentTarget.style.transform = 'translateY(-2px)'; + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = window.isMinimized + ? 'rgba(255,255,255,0.05)' + : 'rgba(255,255,255,0.1)'; + e.currentTarget.style.transform = 'translateY(0)'; + }} + > + {window.icon} + {window.title} + +
+ ))} +
+ ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/components/WallpaperSelector.css b/src/components/WallpaperSelector.css new file mode 100644 index 0000000..53308d1 --- /dev/null +++ b/src/components/WallpaperSelector.css @@ -0,0 +1,117 @@ +.wallpaper-selector { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; +} + +.selector-container { + background: rgba(255, 255, 255, 0.98); + border-radius: 20px; + padding: 50px; + text-align: center; + box-shadow: 0 30px 80px rgba(0,0,0,0.3); + max-width: 1200px; + width: 90%; +} + +.quest-badge { + display: inline-block; + background: linear-gradient(135deg, #667eea, #764ba2); + color: white; + padding: 5px 15px; + border-radius: 20px; + font-size: 14px; + margin-bottom: 20px; +} + +.windows-logo { + font-size: 60px; + margin-bottom: 20px; +} + +.selector-container h1 { + font-size: 32px; + color: #333; + margin-bottom: 10px; +} + +.subtitle { + color: #666; + font-size: 16px; + margin-bottom: 40px; +} + +.wallpaper-options { + display: flex; + gap: 30px; + justify-content: center; + margin-bottom: 50px; + flex-wrap: wrap; +} + +.wallpaper-option { + cursor: pointer; + text-align: center; + transition: all 0.3s ease; + border-radius: 12px; + overflow: hidden; + background: #f5f5f5; +} + +.wallpaper-option:hover { + transform: translateY(-10px); + box-shadow: 0 15px 40px rgba(0,0,0,0.2); +} + +.wallpaper-preview { + width: 280px; + height: 200px; + display: flex; + align-items: center; + justify-content: center; + font-size: 60px; +} + +.xp-preview { + background: linear-gradient(135deg, #3a8c3a 0%, #1e5c1e 100%); +} + +.win7-preview { + background: linear-gradient(135deg, #2d2d2d 0%, #1a1a1a 100%); +} + +.win10-preview { + background: linear-gradient(135deg, #0078d7 0%, #005a9e 100%); +} + +.option-info { + padding: 15px; + background: white; +} + +.option-info h3 { + margin: 0 0 5px 0; + color: #333; + font-size: 18px; +} + +.option-info p { + margin: 0; + color: #666; + font-size: 14px; +} + +.selector-footer { + margin-top: 40px; + padding-top: 20px; + border-top: 1px solid #e0e0e0; + color: #999; + font-size: 14px; +} \ No newline at end of file diff --git a/src/components/WallpaperSelector.tsx b/src/components/WallpaperSelector.tsx new file mode 100644 index 0000000..632cffc --- /dev/null +++ b/src/components/WallpaperSelector.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import type { WallpaperType } from '../App'; +import './WallpaperSelector.css'; + +interface WallpaperSelectorProps { + onSelect: (type: WallpaperType) => void; +} + +const WallpaperSelector: React.FC = ({ onSelect }) => { + return ( +
+
+
День 1
+
🪟
+

Квест: Выбор темы

+

Попробуй разные версии Windows и почувствуй разницу!

+ +
+
onSelect('xp')}> +
🏔️
+
+

Windows XP

+

Классический стиль 2001 года

+
+
+ +
onSelect('win7')}> +
🐟
+
+

Windows 7

+

Современная классика 2009 года

+
+
+ +
onSelect('win10')}> +
🪟
+
+

Windows 10

+

Современный дизайн 2015 года

+
+
+
+ +
+

💡 Подсказка: Выберите любую тему, чтобы продолжить квест

+
+
+
+ ); +}; + +export default WallpaperSelector; diff --git a/src/components/Window.css b/src/components/Window.css new file mode 100644 index 0000000..0c2dce1 --- /dev/null +++ b/src/components/Window.css @@ -0,0 +1,91 @@ +.window { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: 100%; + min-width: 600px; + min-height: 400px; + background: rgb(255, 255, 255); + border-radius: 12px; + box-shadow: 0 20px 60px rgba(0,0,0,0.3); + display: flex; + flex-direction: column; + overflow: hidden; + animation: fadeIn 0.2s ease; + z-index: 100; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translate(-50%, -50%) scale(0.95); + } + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +.window-header { + background: #1a0000; + padding: 12px 16px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #ddd; + cursor: move; +} + +.window-title { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + font-size: 14px; + color: #333; +} + +.window-icon { + font-size: 18px; +} + +.window-controls { + display: flex; + gap: 8px; +} + +.window-btn { + width: 28px; + height: 28px; + border: none; + background: transparent; + cursor: pointer; + border-radius: 4px; + font-size: 14px; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; +} + +.window-btn.minimize:hover { + background: #e0e0e0; +} + +.window-btn.close:hover { + background: #e81123; + color: white; +} + +.window-content { + flex: 1; + overflow: hidden; +} + +.window-iframe { + width: 100%; + height: 100%; + border: none; +} \ No newline at end of file diff --git a/src/components/Window.tsx b/src/components/Window.tsx new file mode 100644 index 0000000..27492c2 --- /dev/null +++ b/src/components/Window.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import './Window.css'; +import type { WindowType } from '../MainApp'; + +interface WindowProps { + window: WindowType; + onClose: (id: string) => void; + onMinimize: (id: string) => void; +} + +const Window: React.FC = ({ window, onClose, onMinimize }) => { + return ( +
+
+
+ {window.icon} + {window.title} +
+
+ + +
+
+
+ {window.content} + {/*