Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 844e8c258b | |||
| dbabee2017 |
+20
-5
@@ -1,8 +1,23 @@
|
|||||||
# Dependencies
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# IDE / System files
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
# You don't want your passwords shared do you?
|
npm-debug.log*
|
||||||
.env
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|||||||
@@ -1,2 +1,70 @@
|
|||||||
# purrgram
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||||
|
|
||||||
|
The page will reload when you make changes.\
|
||||||
|
You may also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||||
|
|
||||||
|
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||||
|
|
||||||
|
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||||
|
|
||||||
|
### Analyzing the Bundle Size
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||||
|
|
||||||
|
### Making a Progressive Web App
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||||
|
|
||||||
|
### `npm run build` fails to minify
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
container_name: learning_platform_db
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: dev_user
|
|
||||||
POSTGRES_PASSWORD: "c21d8e935a40ed83"
|
|
||||||
POSTGRES_DB: learning_platform
|
|
||||||
ports:
|
|
||||||
- "5433:5432"
|
|
||||||
volumes:
|
|
||||||
- pgdata:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
pgadmin:
|
|
||||||
image: dpage/pgadmin4
|
|
||||||
container_name: learning_platform_pgadmin
|
|
||||||
environment:
|
|
||||||
PGADMIN_DEFAULT_EMAIL: ameerkhorsand@proton.me
|
|
||||||
PGADMIN_DEFAULT_PASSWORD: "2f204a6a606e8475"
|
|
||||||
ports:
|
|
||||||
- "5050:80"
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
||||||
Generated
+2752
-989
File diff suppressed because it is too large
Load Diff
+30
-848
@@ -1,857 +1,39 @@
|
|||||||
{
|
{
|
||||||
"name": "purrgram",
|
"name": "your-project-name",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"description": "",
|
"private": true,
|
||||||
"main": "index.js",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"abab": "^2.0.6",
|
"@testing-library/dom": "^10.4.1",
|
||||||
"accepts": "^1.3.8",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"acorn": "^8.16.0",
|
"@testing-library/react": "^16.3.2",
|
||||||
"acorn-globals": "^6.0.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"acorn-import-phases": "^1.0.4",
|
|
||||||
"acorn-jsx": "^5.3.2",
|
|
||||||
"acorn-walk": "^7.2.0",
|
|
||||||
"address": "^1.2.2",
|
|
||||||
"adjust-sourcemap-loader": "^4.0.0",
|
|
||||||
"agent-base": "^6.0.2",
|
|
||||||
"ajv": "^6.15.0",
|
|
||||||
"ajv-formats": "^2.1.1",
|
|
||||||
"ajv-keywords": "^3.5.2",
|
|
||||||
"ansi-escapes": "^4.3.2",
|
|
||||||
"ansi-html": "^0.0.9",
|
|
||||||
"ansi-html-community": "^0.0.8",
|
|
||||||
"ansi-regex": "^5.0.1",
|
|
||||||
"ansi-styles": "^4.3.0",
|
|
||||||
"any-promise": "^1.3.0",
|
|
||||||
"anymatch": "^3.1.3",
|
|
||||||
"arg": "^5.0.2",
|
|
||||||
"argparse": "^1.0.10",
|
|
||||||
"aria-query": "^5.3.2",
|
|
||||||
"array-buffer-byte-length": "^1.0.2",
|
|
||||||
"array-flatten": "^1.1.1",
|
|
||||||
"array-includes": "^3.1.9",
|
|
||||||
"array-union": "^2.1.0",
|
|
||||||
"array.prototype.findlast": "^1.2.5",
|
|
||||||
"array.prototype.findlastindex": "^1.2.6",
|
|
||||||
"array.prototype.flat": "^1.3.3",
|
|
||||||
"array.prototype.flatmap": "^1.3.3",
|
|
||||||
"array.prototype.reduce": "^1.0.8",
|
|
||||||
"array.prototype.tosorted": "^1.1.4",
|
|
||||||
"arraybuffer.prototype.slice": "^1.0.4",
|
|
||||||
"asap": "^2.0.6",
|
|
||||||
"ast-types-flow": "^0.0.8",
|
|
||||||
"async": "^3.2.6",
|
|
||||||
"async-function": "^1.0.0",
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"at-least-node": "^1.0.0",
|
|
||||||
"autoprefixer": "^10.5.0",
|
|
||||||
"available-typed-arrays": "^1.0.7",
|
|
||||||
"axe-core": "^4.11.4",
|
|
||||||
"axobject-query": "^4.1.0",
|
|
||||||
"babel-jest": "^27.5.1",
|
|
||||||
"babel-loader": "^8.4.1",
|
|
||||||
"babel-plugin-istanbul": "^6.1.1",
|
|
||||||
"babel-plugin-jest-hoist": "^27.5.1",
|
|
||||||
"babel-plugin-macros": "^3.1.0",
|
|
||||||
"babel-plugin-named-asset-import": "^0.3.8",
|
|
||||||
"babel-plugin-polyfill-corejs2": "^0.4.17",
|
|
||||||
"babel-plugin-polyfill-corejs3": "^0.14.2",
|
|
||||||
"babel-plugin-polyfill-regenerator": "^0.6.8",
|
|
||||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
|
||||||
"babel-preset-current-node-syntax": "^1.2.0",
|
|
||||||
"babel-preset-jest": "^27.5.1",
|
|
||||||
"babel-preset-react-app": "^10.1.0",
|
|
||||||
"balanced-match": "^1.0.2",
|
|
||||||
"baseline-browser-mapping": "^2.10.31",
|
|
||||||
"batch": "^0.6.1",
|
|
||||||
"bfj": "^7.1.0",
|
|
||||||
"big.js": "^5.2.2",
|
|
||||||
"binary-extensions": "^2.3.0",
|
|
||||||
"bluebird": "^3.7.2",
|
|
||||||
"body-parser": "^1.20.5",
|
|
||||||
"bonjour-service": "^1.3.0",
|
|
||||||
"boolbase": "^1.0.0",
|
|
||||||
"brace-expansion": "^1.1.14",
|
|
||||||
"braces": "^3.0.3",
|
|
||||||
"browser-process-hrtime": "^1.0.0",
|
|
||||||
"browserslist": "^4.28.2",
|
|
||||||
"bser": "^2.1.1",
|
|
||||||
"buffer-from": "^1.1.2",
|
|
||||||
"builtin-modules": "^3.3.0",
|
|
||||||
"bytes": "^3.1.2",
|
|
||||||
"call-bind": "^1.0.9",
|
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
|
||||||
"call-bound": "^1.0.4",
|
|
||||||
"callsites": "^3.1.0",
|
|
||||||
"camel-case": "^4.1.2",
|
|
||||||
"camelcase": "^6.3.0",
|
|
||||||
"camelcase-css": "^2.0.1",
|
|
||||||
"caniuse-api": "^3.0.0",
|
|
||||||
"caniuse-lite": "^1.0.30001793",
|
|
||||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
|
||||||
"chalk": "^4.1.2",
|
|
||||||
"char-regex": "^1.0.2",
|
|
||||||
"check-types": "^11.2.3",
|
|
||||||
"chokidar": "^3.6.0",
|
|
||||||
"chrome-trace-event": "^1.0.4",
|
|
||||||
"ci-info": "^3.9.0",
|
|
||||||
"cjs-module-lexer": "^1.4.3",
|
|
||||||
"clean-css": "^5.3.3",
|
|
||||||
"cliui": "^7.0.4",
|
|
||||||
"co": "^4.6.0",
|
|
||||||
"coa": "^2.0.2",
|
|
||||||
"collect-v8-coverage": "^1.0.3",
|
|
||||||
"color-convert": "^2.0.1",
|
|
||||||
"color-name": "^1.1.4",
|
|
||||||
"colord": "^2.9.3",
|
|
||||||
"colorette": "^2.0.20",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"commander": "^8.3.0",
|
|
||||||
"common-tags": "^1.8.2",
|
|
||||||
"commondir": "^1.0.1",
|
|
||||||
"compressible": "^2.0.18",
|
|
||||||
"compression": "^1.8.1",
|
|
||||||
"concat-map": "^0.0.1",
|
|
||||||
"confusing-browser-globals": "^1.0.11",
|
|
||||||
"connect-history-api-fallback": "^2.0.0",
|
|
||||||
"content-disposition": "^0.5.4",
|
|
||||||
"content-type": "^1.0.5",
|
|
||||||
"convert-source-map": "^2.0.0",
|
|
||||||
"cookie": "^0.7.2",
|
|
||||||
"cookie-signature": "^1.0.7",
|
|
||||||
"core-js": "^3.49.0",
|
|
||||||
"core-js-compat": "^3.49.0",
|
|
||||||
"core-js-pure": "^3.49.0",
|
|
||||||
"core-util-is": "^1.0.3",
|
|
||||||
"cosmiconfig": "^7.1.0",
|
|
||||||
"cross-spawn": "^7.0.6",
|
|
||||||
"crypto-random-string": "^2.0.0",
|
|
||||||
"css-blank-pseudo": "^3.0.3",
|
|
||||||
"css-declaration-sorter": "^6.4.1",
|
|
||||||
"css-has-pseudo": "^3.0.4",
|
|
||||||
"css-loader": "^6.11.0",
|
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
|
||||||
"css-prefers-color-scheme": "^6.0.3",
|
|
||||||
"css-select": "^4.3.0",
|
|
||||||
"css-select-base-adapter": "^0.1.1",
|
|
||||||
"css-tree": "^1.0.0-alpha.37",
|
|
||||||
"css-what": "^6.2.2",
|
|
||||||
"css.escape": "^1.5.1",
|
|
||||||
"cssdb": "^7.11.2",
|
|
||||||
"cssesc": "^3.0.0",
|
|
||||||
"cssnano": "^5.1.15",
|
|
||||||
"cssnano-preset-default": "^5.2.14",
|
|
||||||
"cssnano-utils": "^3.1.0",
|
|
||||||
"csso": "^4.2.0",
|
|
||||||
"cssom": "^0.4.4",
|
|
||||||
"cssstyle": "^2.3.0",
|
|
||||||
"damerau-levenshtein": "^1.0.8",
|
|
||||||
"data-urls": "^2.0.0",
|
|
||||||
"data-view-buffer": "^1.0.2",
|
|
||||||
"data-view-byte-length": "^1.0.2",
|
|
||||||
"data-view-byte-offset": "^1.0.1",
|
|
||||||
"debug": "^4.4.3",
|
|
||||||
"decimal.js": "^10.6.0",
|
|
||||||
"dedent": "^0.7.0",
|
|
||||||
"deep-is": "^0.1.4",
|
|
||||||
"deepmerge": "^4.3.1",
|
|
||||||
"default-gateway": "^6.0.3",
|
|
||||||
"define-data-property": "^1.1.4",
|
|
||||||
"define-lazy-prop": "^2.0.0",
|
|
||||||
"define-properties": "^1.2.1",
|
|
||||||
"delayed-stream": "^1.0.0",
|
|
||||||
"depd": "^2.0.0",
|
|
||||||
"dequal": "^2.0.3",
|
|
||||||
"destroy": "^1.2.0",
|
|
||||||
"detect-newline": "^3.1.0",
|
|
||||||
"detect-node": "^2.1.0",
|
|
||||||
"detect-port-alt": "^1.1.6",
|
|
||||||
"didyoumean": "^1.2.2",
|
|
||||||
"diff-sequences": "^27.5.1",
|
|
||||||
"dir-glob": "^3.0.1",
|
|
||||||
"dlv": "^1.1.3",
|
|
||||||
"dns-packet": "^5.6.1",
|
|
||||||
"doctrine": "^3.0.0",
|
|
||||||
"dom-accessibility-api": "^0.5.16",
|
|
||||||
"dom-converter": "^0.2.0",
|
|
||||||
"dom-serializer": "^1.4.1",
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domexception": "^2.0.1",
|
|
||||||
"domhandler": "^4.3.1",
|
|
||||||
"domutils": "^2.8.0",
|
|
||||||
"dot-case": "^3.0.4",
|
|
||||||
"dotenv": "^10.0.0",
|
|
||||||
"dotenv-expand": "^5.1.0",
|
|
||||||
"dunder-proto": "^1.0.1",
|
|
||||||
"duplexer": "^0.1.2",
|
|
||||||
"ee-first": "^1.1.1",
|
|
||||||
"ejs": "^3.1.10",
|
|
||||||
"electron-to-chromium": "^1.5.360",
|
|
||||||
"emittery": "^0.8.1",
|
|
||||||
"emoji-regex": "^9.2.2",
|
|
||||||
"emojis-list": "^3.0.0",
|
|
||||||
"encodeurl": "^2.0.0",
|
|
||||||
"enhanced-resolve": "^5.21.6",
|
|
||||||
"entities": "^2.2.0",
|
|
||||||
"error-ex": "^1.3.4",
|
|
||||||
"error-stack-parser": "^2.1.4",
|
|
||||||
"es-abstract": "^1.24.2",
|
|
||||||
"es-array-method-boxes-properly": "^1.0.0",
|
|
||||||
"es-define-property": "^1.0.1",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"es-iterator-helpers": "^1.3.2",
|
|
||||||
"es-module-lexer": "^2.1.0",
|
|
||||||
"es-object-atoms": "^1.1.1",
|
|
||||||
"es-set-tostringtag": "^2.1.0",
|
|
||||||
"es-shim-unscopables": "^1.1.0",
|
|
||||||
"es-to-primitive": "^1.3.0",
|
|
||||||
"escalade": "^3.2.0",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"escape-string-regexp": "^4.0.0",
|
|
||||||
"escodegen": "^2.1.0",
|
|
||||||
"eslint": "^8.57.1",
|
|
||||||
"eslint-config-react-app": "^7.0.1",
|
|
||||||
"eslint-import-resolver-node": "^0.3.10",
|
|
||||||
"eslint-module-utils": "^2.12.1",
|
|
||||||
"eslint-plugin-flowtype": "^8.0.3",
|
|
||||||
"eslint-plugin-import": "^2.32.0",
|
|
||||||
"eslint-plugin-jest": "^25.7.0",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
||||||
"eslint-plugin-react": "^7.37.5",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
|
||||||
"eslint-plugin-testing-library": "^5.11.1",
|
|
||||||
"eslint-scope": "^7.2.2",
|
|
||||||
"eslint-visitor-keys": "^3.4.3",
|
|
||||||
"eslint-webpack-plugin": "^3.2.0",
|
|
||||||
"espree": "^9.6.1",
|
|
||||||
"esprima": "^4.0.1",
|
|
||||||
"esquery": "^1.7.0",
|
|
||||||
"esrecurse": "^4.3.0",
|
|
||||||
"estraverse": "^5.3.0",
|
|
||||||
"estree-walker": "^1.0.1",
|
|
||||||
"esutils": "^2.0.3",
|
|
||||||
"etag": "^1.8.1",
|
|
||||||
"eventemitter3": "^4.0.7",
|
|
||||||
"events": "^3.3.0",
|
|
||||||
"execa": "^5.1.1",
|
|
||||||
"exit": "^0.1.2",
|
|
||||||
"expect": "^27.5.1",
|
|
||||||
"express": "^4.22.2",
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"fast-glob": "^3.3.3",
|
|
||||||
"fast-json-stable-stringify": "^2.1.0",
|
|
||||||
"fast-levenshtein": "^2.0.6",
|
|
||||||
"fast-uri": "^3.1.2",
|
|
||||||
"fastq": "^1.20.1",
|
|
||||||
"faye-websocket": "^0.11.4",
|
|
||||||
"fb-watchman": "^2.0.2",
|
|
||||||
"file-entry-cache": "^6.0.1",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"filelist": "^1.0.6",
|
|
||||||
"filesize": "^8.0.7",
|
|
||||||
"fill-range": "^7.1.1",
|
|
||||||
"finalhandler": "^1.3.2",
|
|
||||||
"find-cache-dir": "^3.3.2",
|
|
||||||
"find-up": "^4.1.0",
|
|
||||||
"flat-cache": "^3.2.0",
|
|
||||||
"flatted": "^3.4.2",
|
|
||||||
"follow-redirects": "^1.16.0",
|
|
||||||
"for-each": "^0.3.5",
|
|
||||||
"fork-ts-checker-webpack-plugin": "^6.5.3",
|
|
||||||
"form-data": "^3.0.4",
|
|
||||||
"forwarded": "^0.2.0",
|
|
||||||
"fraction.js": "^5.3.4",
|
|
||||||
"fresh": "^0.5.2",
|
|
||||||
"fs-extra": "^10.1.0",
|
|
||||||
"fs-monkey": "^1.1.0",
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"function-bind": "^1.1.2",
|
|
||||||
"function.prototype.name": "^1.1.8",
|
|
||||||
"functions-have-names": "^1.2.3",
|
|
||||||
"generator-function": "^2.0.1",
|
|
||||||
"gensync": "^1.0.0-beta.2",
|
|
||||||
"get-caller-file": "^2.0.5",
|
|
||||||
"get-intrinsic": "^1.3.0",
|
|
||||||
"get-own-enumerable-property-symbols": "^3.0.2",
|
|
||||||
"get-package-type": "^0.1.0",
|
|
||||||
"get-proto": "^1.0.1",
|
|
||||||
"get-stream": "^6.0.1",
|
|
||||||
"get-symbol-description": "^1.1.0",
|
|
||||||
"glob": "^7.2.3",
|
|
||||||
"glob-parent": "^6.0.2",
|
|
||||||
"glob-to-regexp": "^0.4.1",
|
|
||||||
"global-modules": "^2.0.0",
|
|
||||||
"global-prefix": "^3.0.0",
|
|
||||||
"globals": "^13.24.0",
|
|
||||||
"globalthis": "^1.0.4",
|
|
||||||
"globby": "^11.1.0",
|
|
||||||
"gopd": "^1.2.0",
|
|
||||||
"graceful-fs": "^4.2.11",
|
|
||||||
"graphemer": "^1.4.0",
|
|
||||||
"gzip-size": "^6.0.0",
|
|
||||||
"handle-thing": "^2.0.1",
|
|
||||||
"harmony-reflect": "^1.6.2",
|
|
||||||
"has-bigints": "^1.1.0",
|
|
||||||
"has-flag": "^4.0.0",
|
|
||||||
"has-property-descriptors": "^1.0.2",
|
|
||||||
"has-proto": "^1.2.0",
|
|
||||||
"has-symbols": "^1.1.0",
|
|
||||||
"has-tostringtag": "^1.0.2",
|
|
||||||
"hasown": "^2.0.3",
|
|
||||||
"he": "^1.2.0",
|
|
||||||
"hoopy": "^0.1.4",
|
|
||||||
"hpack.js": "^2.1.6",
|
|
||||||
"html-encoding-sniffer": "^2.0.1",
|
|
||||||
"html-entities": "^2.6.0",
|
|
||||||
"html-escaper": "^2.0.2",
|
|
||||||
"html-minifier-terser": "^6.1.0",
|
|
||||||
"html-webpack-plugin": "^5.6.7",
|
|
||||||
"htmlparser2": "^6.1.0",
|
|
||||||
"http-deceiver": "^1.2.7",
|
|
||||||
"http-errors": "^2.0.1",
|
|
||||||
"http-parser-js": "^0.5.10",
|
|
||||||
"http-proxy": "^1.18.1",
|
|
||||||
"http-proxy-agent": "^4.0.1",
|
|
||||||
"http-proxy-middleware": "^2.0.9",
|
|
||||||
"https-proxy-agent": "^5.0.1",
|
|
||||||
"human-signals": "^2.1.0",
|
|
||||||
"iconv-lite": "^0.6.3",
|
|
||||||
"icss-utils": "^5.1.0",
|
|
||||||
"idb": "^7.1.1",
|
|
||||||
"identity-obj-proxy": "^3.0.0",
|
|
||||||
"ignore": "^5.3.2",
|
|
||||||
"immer": "^9.0.21",
|
|
||||||
"import-fresh": "^3.3.1",
|
|
||||||
"import-local": "^3.2.0",
|
|
||||||
"imurmurhash": "^0.1.4",
|
|
||||||
"indent-string": "^4.0.0",
|
|
||||||
"inflight": "^1.0.6",
|
|
||||||
"inherits": "^2.0.4",
|
|
||||||
"ini": "^1.3.8",
|
|
||||||
"internal-slot": "^1.1.0",
|
|
||||||
"ipaddr.js": "^2.4.0",
|
|
||||||
"is-array-buffer": "^3.0.5",
|
|
||||||
"is-arrayish": "^0.2.1",
|
|
||||||
"is-async-function": "^2.1.1",
|
|
||||||
"is-bigint": "^1.1.0",
|
|
||||||
"is-binary-path": "^2.1.0",
|
|
||||||
"is-boolean-object": "^1.2.2",
|
|
||||||
"is-callable": "^1.2.7",
|
|
||||||
"is-core-module": "^2.16.2",
|
|
||||||
"is-data-view": "^1.0.2",
|
|
||||||
"is-date-object": "^1.1.0",
|
|
||||||
"is-docker": "^2.2.1",
|
|
||||||
"is-extglob": "^2.1.1",
|
|
||||||
"is-finalizationregistry": "^1.1.1",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"is-generator-fn": "^2.1.0",
|
|
||||||
"is-generator-function": "^1.1.2",
|
|
||||||
"is-glob": "^4.0.3",
|
|
||||||
"is-map": "^2.0.3",
|
|
||||||
"is-module": "^1.0.0",
|
|
||||||
"is-negative-zero": "^2.0.3",
|
|
||||||
"is-number": "^7.0.0",
|
|
||||||
"is-number-object": "^1.1.1",
|
|
||||||
"is-obj": "^1.0.1",
|
|
||||||
"is-path-inside": "^3.0.3",
|
|
||||||
"is-plain-obj": "^3.0.0",
|
|
||||||
"is-potential-custom-element-name": "^1.0.1",
|
|
||||||
"is-regex": "^1.2.1",
|
|
||||||
"is-regexp": "^1.0.0",
|
|
||||||
"is-root": "^2.1.0",
|
|
||||||
"is-set": "^2.0.3",
|
|
||||||
"is-shared-array-buffer": "^1.0.4",
|
|
||||||
"is-stream": "^2.0.1",
|
|
||||||
"is-string": "^1.1.1",
|
|
||||||
"is-symbol": "^1.1.1",
|
|
||||||
"is-typed-array": "^1.1.15",
|
|
||||||
"is-typedarray": "^1.0.0",
|
|
||||||
"is-weakmap": "^2.0.2",
|
|
||||||
"is-weakref": "^1.1.1",
|
|
||||||
"is-weakset": "^2.0.4",
|
|
||||||
"is-wsl": "^2.2.0",
|
|
||||||
"isarray": "^2.0.5",
|
|
||||||
"isexe": "^2.0.0",
|
|
||||||
"istanbul-lib-coverage": "^3.2.2",
|
|
||||||
"istanbul-lib-instrument": "^5.2.1",
|
|
||||||
"istanbul-lib-report": "^3.0.1",
|
|
||||||
"istanbul-lib-source-maps": "^4.0.1",
|
|
||||||
"istanbul-reports": "^3.2.0",
|
|
||||||
"iterator.prototype": "^1.1.5",
|
|
||||||
"jake": "^10.9.4",
|
|
||||||
"jest-changed-files": "^27.5.1",
|
|
||||||
"jest-circus": "^27.5.1",
|
|
||||||
"jest-cli": "^27.5.1",
|
|
||||||
"jest-config": "^27.5.1",
|
|
||||||
"jest-diff": "^27.5.1",
|
|
||||||
"jest-docblock": "^27.5.1",
|
|
||||||
"jest-each": "^27.5.1",
|
|
||||||
"jest-environment-jsdom": "^27.5.1",
|
|
||||||
"jest-environment-node": "^27.5.1",
|
|
||||||
"jest-get-type": "^27.5.1",
|
|
||||||
"jest-haste-map": "^27.5.1",
|
|
||||||
"jest-jasmine2": "^27.5.1",
|
|
||||||
"jest-leak-detector": "^27.5.1",
|
|
||||||
"jest-matcher-utils": "^27.5.1",
|
|
||||||
"jest-message-util": "^27.5.1",
|
|
||||||
"jest-mock": "^27.5.1",
|
|
||||||
"jest-pnp-resolver": "^1.2.3",
|
|
||||||
"jest-regex-util": "^27.5.1",
|
|
||||||
"jest-resolve": "^27.5.1",
|
|
||||||
"jest-resolve-dependencies": "^27.5.1",
|
|
||||||
"jest-runner": "^27.5.1",
|
|
||||||
"jest-runtime": "^27.5.1",
|
|
||||||
"jest-serializer": "^27.5.1",
|
|
||||||
"jest-snapshot": "^27.5.1",
|
|
||||||
"jest-util": "^27.5.1",
|
|
||||||
"jest-validate": "^27.5.1",
|
|
||||||
"jest-watch-typeahead": "^1.1.0",
|
|
||||||
"jest-watcher": "^27.5.1",
|
|
||||||
"jest-worker": "^27.5.1",
|
|
||||||
"jiti": "^1.21.7",
|
|
||||||
"js-tokens": "^4.0.0",
|
|
||||||
"js-yaml": "^3.14.2",
|
|
||||||
"jsdom": "^16.7.0",
|
|
||||||
"jsesc": "^3.1.0",
|
|
||||||
"json-buffer": "^3.0.1",
|
|
||||||
"json-parse-even-better-errors": "^2.3.1",
|
|
||||||
"json-schema-traverse": "^0.4.1",
|
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
|
||||||
"json5": "^2.2.3",
|
|
||||||
"jsonfile": "^6.2.1",
|
|
||||||
"jsonpath": "^1.3.0",
|
|
||||||
"jsonpointer": "^5.0.1",
|
|
||||||
"jsx-ast-utils": "^3.3.5",
|
|
||||||
"keyv": "^4.5.4",
|
|
||||||
"kind-of": "^6.0.3",
|
|
||||||
"kleur": "^3.0.3",
|
|
||||||
"klona": "^2.0.6",
|
|
||||||
"language-subtag-registry": "^0.3.23",
|
|
||||||
"language-tags": "^1.0.9",
|
|
||||||
"launch-editor": "^2.13.2",
|
|
||||||
"leven": "^3.1.0",
|
|
||||||
"levn": "^0.4.1",
|
|
||||||
"lilconfig": "^2.1.0",
|
|
||||||
"lines-and-columns": "^1.2.4",
|
|
||||||
"loader-runner": "^4.3.2",
|
|
||||||
"loader-utils": "^2.0.4",
|
|
||||||
"locate-path": "^5.0.0",
|
|
||||||
"lodash": "^4.18.1",
|
|
||||||
"lodash.debounce": "^4.0.8",
|
|
||||||
"lodash.memoize": "^4.1.2",
|
|
||||||
"lodash.merge": "^4.6.2",
|
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"lodash.uniq": "^4.5.0",
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"lower-case": "^2.0.2",
|
|
||||||
"lru-cache": "^5.1.1",
|
|
||||||
"lz-string": "^1.5.0",
|
|
||||||
"magic-string": "^0.25.9",
|
|
||||||
"make-dir": "^3.1.0",
|
|
||||||
"makeerror": "^1.0.12",
|
|
||||||
"math-intrinsics": "^1.1.0",
|
|
||||||
"mdn-data": "^2.0.4",
|
|
||||||
"media-typer": "^0.3.0",
|
|
||||||
"memfs": "^3.5.3",
|
|
||||||
"merge-descriptors": "^1.0.3",
|
|
||||||
"merge-stream": "^2.0.0",
|
|
||||||
"merge2": "^1.4.1",
|
|
||||||
"methods": "^1.1.2",
|
|
||||||
"micromatch": "^4.0.8",
|
|
||||||
"mime": "^1.6.0",
|
|
||||||
"mime-db": "^1.52.0",
|
|
||||||
"mime-types": "^2.1.35",
|
|
||||||
"mimic-fn": "^2.1.0",
|
|
||||||
"min-indent": "^1.0.1",
|
|
||||||
"mini-css-extract-plugin": "^2.10.2",
|
|
||||||
"minimalistic-assert": "^1.0.1",
|
|
||||||
"minimatch": "^3.1.5",
|
|
||||||
"minimist": "^1.2.8",
|
|
||||||
"mkdirp": "^0.5.6",
|
|
||||||
"ms": "^2.1.3",
|
|
||||||
"multicast-dns": "^7.2.5",
|
|
||||||
"mz": "^2.7.0",
|
|
||||||
"nanoid": "^3.3.12",
|
|
||||||
"natural-compare": "^1.4.0",
|
|
||||||
"natural-compare-lite": "^1.4.0",
|
|
||||||
"negotiator": "^0.6.4",
|
|
||||||
"neo-async": "^2.6.2",
|
|
||||||
"no-case": "^3.0.4",
|
|
||||||
"node-exports-info": "^1.6.0",
|
|
||||||
"node-forge": "^1.4.0",
|
|
||||||
"node-int64": "^0.4.0",
|
|
||||||
"node-releases": "^2.0.45",
|
|
||||||
"normalize-path": "^3.0.0",
|
|
||||||
"normalize-url": "^6.1.0",
|
|
||||||
"npm-run-path": "^4.0.1",
|
|
||||||
"nth-check": "^2.1.1",
|
|
||||||
"nwsapi": "^2.2.23",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"object-hash": "^3.0.0",
|
|
||||||
"object-inspect": "^1.13.4",
|
|
||||||
"object-keys": "^1.1.1",
|
|
||||||
"object.assign": "^4.1.7",
|
|
||||||
"object.entries": "^1.1.9",
|
|
||||||
"object.fromentries": "^2.0.8",
|
|
||||||
"object.getownpropertydescriptors": "^2.1.9",
|
|
||||||
"object.groupby": "^1.0.3",
|
|
||||||
"object.values": "^1.2.1",
|
|
||||||
"obuf": "^1.1.2",
|
|
||||||
"on-finished": "^2.4.1",
|
|
||||||
"on-headers": "^1.1.0",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"onetime": "^5.1.2",
|
|
||||||
"open": "^8.4.2",
|
|
||||||
"optionator": "^0.9.4",
|
|
||||||
"own-keys": "^1.0.1",
|
|
||||||
"p-limit": "^2.3.0",
|
|
||||||
"p-locate": "^4.1.0",
|
|
||||||
"p-retry": "^4.6.2",
|
|
||||||
"p-try": "^2.2.0",
|
|
||||||
"param-case": "^3.0.4",
|
|
||||||
"parent-module": "^1.0.1",
|
|
||||||
"parse-json": "^5.2.0",
|
|
||||||
"parse5": "^6.0.1",
|
|
||||||
"parseurl": "^1.3.3",
|
|
||||||
"pascal-case": "^3.1.2",
|
|
||||||
"path-exists": "^4.0.0",
|
|
||||||
"path-is-absolute": "^1.0.1",
|
|
||||||
"path-key": "^3.1.1",
|
|
||||||
"path-parse": "^1.0.7",
|
|
||||||
"path-to-regexp": "^0.1.13",
|
|
||||||
"path-type": "^4.0.0",
|
|
||||||
"performance-now": "^2.1.0",
|
|
||||||
"pg": "^8.21.0",
|
|
||||||
"picocolors": "^1.1.1",
|
|
||||||
"picomatch": "^2.3.2",
|
|
||||||
"pify": "^2.3.0",
|
|
||||||
"pirates": "^4.0.7",
|
|
||||||
"pkg-dir": "^4.2.0",
|
|
||||||
"pkg-up": "^3.1.0",
|
|
||||||
"possible-typed-array-names": "^1.1.0",
|
|
||||||
"postcss": "^8.5.15",
|
|
||||||
"postcss-attribute-case-insensitive": "^5.0.2",
|
|
||||||
"postcss-browser-comments": "^4.0.0",
|
|
||||||
"postcss-calc": "^8.2.4",
|
|
||||||
"postcss-clamp": "^4.1.0",
|
|
||||||
"postcss-color-functional-notation": "^4.2.4",
|
|
||||||
"postcss-color-hex-alpha": "^8.0.4",
|
|
||||||
"postcss-color-rebeccapurple": "^7.1.1",
|
|
||||||
"postcss-colormin": "^5.3.1",
|
|
||||||
"postcss-convert-values": "^5.1.3",
|
|
||||||
"postcss-custom-media": "^8.0.2",
|
|
||||||
"postcss-custom-properties": "^12.1.11",
|
|
||||||
"postcss-custom-selectors": "^6.0.3",
|
|
||||||
"postcss-dir-pseudo-class": "^6.0.5",
|
|
||||||
"postcss-discard-comments": "^5.1.2",
|
|
||||||
"postcss-discard-duplicates": "^5.1.0",
|
|
||||||
"postcss-discard-empty": "^5.1.1",
|
|
||||||
"postcss-discard-overridden": "^5.1.0",
|
|
||||||
"postcss-double-position-gradients": "^3.1.2",
|
|
||||||
"postcss-env-function": "^4.0.6",
|
|
||||||
"postcss-flexbugs-fixes": "^5.0.2",
|
|
||||||
"postcss-focus-visible": "^6.0.4",
|
|
||||||
"postcss-focus-within": "^5.0.4",
|
|
||||||
"postcss-font-variant": "^5.0.0",
|
|
||||||
"postcss-gap-properties": "^3.0.5",
|
|
||||||
"postcss-image-set-function": "^4.0.7",
|
|
||||||
"postcss-import": "^15.1.0",
|
|
||||||
"postcss-initial": "^4.0.1",
|
|
||||||
"postcss-js": "^4.1.0",
|
|
||||||
"postcss-lab-function": "^4.2.1",
|
|
||||||
"postcss-loader": "^6.2.1",
|
|
||||||
"postcss-logical": "^5.0.4",
|
|
||||||
"postcss-media-minmax": "^5.0.0",
|
|
||||||
"postcss-merge-longhand": "^5.1.7",
|
|
||||||
"postcss-merge-rules": "^5.1.4",
|
|
||||||
"postcss-minify-font-values": "^5.1.0",
|
|
||||||
"postcss-minify-gradients": "^5.1.1",
|
|
||||||
"postcss-minify-params": "^5.1.4",
|
|
||||||
"postcss-minify-selectors": "^5.2.1",
|
|
||||||
"postcss-modules-extract-imports": "^3.1.0",
|
|
||||||
"postcss-modules-local-by-default": "^4.2.0",
|
|
||||||
"postcss-modules-scope": "^3.2.1",
|
|
||||||
"postcss-modules-values": "^4.0.0",
|
|
||||||
"postcss-nested": "^6.2.0",
|
|
||||||
"postcss-nesting": "^10.2.0",
|
|
||||||
"postcss-normalize": "^10.0.1",
|
|
||||||
"postcss-normalize-charset": "^5.1.0",
|
|
||||||
"postcss-normalize-display-values": "^5.1.0",
|
|
||||||
"postcss-normalize-positions": "^5.1.1",
|
|
||||||
"postcss-normalize-repeat-style": "^5.1.1",
|
|
||||||
"postcss-normalize-string": "^5.1.0",
|
|
||||||
"postcss-normalize-timing-functions": "^5.1.0",
|
|
||||||
"postcss-normalize-unicode": "^5.1.1",
|
|
||||||
"postcss-normalize-url": "^5.1.0",
|
|
||||||
"postcss-normalize-whitespace": "^5.1.1",
|
|
||||||
"postcss-opacity-percentage": "^1.1.3",
|
|
||||||
"postcss-ordered-values": "^5.1.3",
|
|
||||||
"postcss-overflow-shorthand": "^3.0.4",
|
|
||||||
"postcss-page-break": "^3.0.4",
|
|
||||||
"postcss-place": "^7.0.5",
|
|
||||||
"postcss-preset-env": "^7.8.3",
|
|
||||||
"postcss-pseudo-class-any-link": "^7.1.6",
|
|
||||||
"postcss-reduce-initial": "^5.1.2",
|
|
||||||
"postcss-reduce-transforms": "^5.1.0",
|
|
||||||
"postcss-replace-overflow-wrap": "^4.0.0",
|
|
||||||
"postcss-selector-not": "^6.0.1",
|
|
||||||
"postcss-selector-parser": "^6.1.2",
|
|
||||||
"postcss-svgo": "^5.1.0",
|
|
||||||
"postcss-unique-selectors": "^5.1.1",
|
|
||||||
"postcss-value-parser": "^4.2.0",
|
|
||||||
"prelude-ls": "^1.2.1",
|
|
||||||
"pretty-bytes": "^5.6.0",
|
|
||||||
"pretty-error": "^4.0.0",
|
|
||||||
"pretty-format": "^27.5.1",
|
|
||||||
"process-nextick-args": "^2.0.1",
|
|
||||||
"promise": "^8.3.0",
|
|
||||||
"prompts": "^2.4.2",
|
|
||||||
"prop-types": "^15.8.1",
|
|
||||||
"proxy-addr": "^2.0.7",
|
|
||||||
"psl": "^1.15.0",
|
|
||||||
"punycode": "^2.3.1",
|
|
||||||
"q": "^1.5.1",
|
|
||||||
"qs": "^6.15.2",
|
|
||||||
"querystringify": "^2.2.0",
|
|
||||||
"queue-microtask": "^1.2.3",
|
|
||||||
"raf": "^3.4.1",
|
|
||||||
"randombytes": "^2.1.0",
|
|
||||||
"range-parser": "^1.2.1",
|
|
||||||
"raw-body": "^2.5.3",
|
|
||||||
"react": "^19.2.6",
|
"react": "^19.2.6",
|
||||||
"react-app-polyfill": "^3.0.0",
|
|
||||||
"react-dev-utils": "^12.0.1",
|
|
||||||
"react-dom": "^19.2.6",
|
"react-dom": "^19.2.6",
|
||||||
"react-error-overlay": "^6.1.0",
|
"react-scripts": "5.0.1",
|
||||||
"react-is": "^17.0.2",
|
"web-vitals": "^2.1.4"
|
||||||
"react-refresh": "^0.11.0",
|
|
||||||
"react-scripts": "^5.0.1",
|
|
||||||
"read-cache": "^1.0.0",
|
|
||||||
"readable-stream": "^3.6.2",
|
|
||||||
"readdirp": "^3.6.0",
|
|
||||||
"recursive-readdir": "^2.2.3",
|
|
||||||
"redent": "^3.0.0",
|
|
||||||
"reflect.getprototypeof": "^1.0.10",
|
|
||||||
"regenerate": "^1.4.2",
|
|
||||||
"regenerate-unicode-properties": "^10.2.2",
|
|
||||||
"regenerator-runtime": "^0.13.11",
|
|
||||||
"regex-parser": "^2.3.1",
|
|
||||||
"regexp.prototype.flags": "^1.5.4",
|
|
||||||
"regexpu-core": "^6.4.0",
|
|
||||||
"regjsgen": "^0.8.0",
|
|
||||||
"regjsparser": "^0.13.1",
|
|
||||||
"relateurl": "^0.2.7",
|
|
||||||
"renderkid": "^3.0.0",
|
|
||||||
"require-directory": "^2.1.1",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"requires-port": "^1.0.0",
|
|
||||||
"resolve": "^1.22.12",
|
|
||||||
"resolve-cwd": "^3.0.0",
|
|
||||||
"resolve-from": "^5.0.0",
|
|
||||||
"resolve-url-loader": "^4.0.0",
|
|
||||||
"resolve.exports": "^1.1.1",
|
|
||||||
"retry": "^0.13.1",
|
|
||||||
"reusify": "^1.1.0",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"rollup": "^2.80.0",
|
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
|
||||||
"run-parallel": "^1.2.0",
|
|
||||||
"safe-array-concat": "^1.1.4",
|
|
||||||
"safe-buffer": "^5.2.1",
|
|
||||||
"safe-push-apply": "^1.0.0",
|
|
||||||
"safe-regex-test": "^1.1.0",
|
|
||||||
"safer-buffer": "^2.1.2",
|
|
||||||
"sanitize.css": "^13.0.0",
|
|
||||||
"sass-loader": "^12.6.0",
|
|
||||||
"sax": "^1.2.4",
|
|
||||||
"saxes": "^5.0.1",
|
|
||||||
"scheduler": "^0.27.0",
|
|
||||||
"schema-utils": "^4.3.3",
|
|
||||||
"select-hose": "^2.0.0",
|
|
||||||
"selfsigned": "^2.4.1",
|
|
||||||
"semver": "^7.8.0",
|
|
||||||
"send": "^0.19.2",
|
|
||||||
"serialize-javascript": "^6.0.2",
|
|
||||||
"serve-index": "^1.9.2",
|
|
||||||
"serve-static": "^1.16.3",
|
|
||||||
"set-function-length": "^1.2.2",
|
|
||||||
"set-function-name": "^2.0.2",
|
|
||||||
"set-proto": "^1.0.0",
|
|
||||||
"setprototypeof": "^1.2.0",
|
|
||||||
"shebang-command": "^2.0.0",
|
|
||||||
"shebang-regex": "^3.0.0",
|
|
||||||
"shell-quote": "^1.8.3",
|
|
||||||
"side-channel": "^1.1.0",
|
|
||||||
"side-channel-list": "^1.0.1",
|
|
||||||
"side-channel-map": "^1.0.1",
|
|
||||||
"side-channel-weakmap": "^1.0.2",
|
|
||||||
"signal-exit": "^3.0.7",
|
|
||||||
"sisteransi": "^1.0.5",
|
|
||||||
"slash": "^3.0.0",
|
|
||||||
"sockjs": "^0.3.24",
|
|
||||||
"source-list-map": "^2.0.1",
|
|
||||||
"source-map": "^0.7.6",
|
|
||||||
"source-map-js": "^1.2.1",
|
|
||||||
"source-map-loader": "^3.0.2",
|
|
||||||
"source-map-support": "^0.5.21",
|
|
||||||
"sourcemap-codec": "^1.4.8",
|
|
||||||
"spdy": "^4.0.2",
|
|
||||||
"spdy-transport": "^3.0.0",
|
|
||||||
"sprintf-js": "^1.0.3",
|
|
||||||
"stable": "^0.1.8",
|
|
||||||
"stack-utils": "^2.0.6",
|
|
||||||
"stackframe": "^1.3.4",
|
|
||||||
"static-eval": "^2.1.1",
|
|
||||||
"statuses": "^2.0.2",
|
|
||||||
"stop-iteration-iterator": "^1.1.0",
|
|
||||||
"string_decoder": "^1.3.0",
|
|
||||||
"string-length": "^4.0.2",
|
|
||||||
"string-natural-compare": "^3.0.1",
|
|
||||||
"string-width": "^4.2.3",
|
|
||||||
"string.prototype.includes": "^2.0.1",
|
|
||||||
"string.prototype.matchall": "^4.0.12",
|
|
||||||
"string.prototype.repeat": "^1.0.0",
|
|
||||||
"string.prototype.trim": "^1.2.10",
|
|
||||||
"string.prototype.trimend": "^1.0.9",
|
|
||||||
"string.prototype.trimstart": "^1.0.8",
|
|
||||||
"stringify-object": "^3.3.0",
|
|
||||||
"strip-ansi": "^6.0.1",
|
|
||||||
"strip-bom": "^4.0.0",
|
|
||||||
"strip-comments": "^2.0.1",
|
|
||||||
"strip-final-newline": "^2.0.0",
|
|
||||||
"strip-indent": "^3.0.0",
|
|
||||||
"strip-json-comments": "^3.1.1",
|
|
||||||
"style-loader": "^3.3.4",
|
|
||||||
"stylehacks": "^5.1.1",
|
|
||||||
"sucrase": "^3.35.1",
|
|
||||||
"supports-color": "^7.2.0",
|
|
||||||
"supports-hyperlinks": "^2.3.0",
|
|
||||||
"supports-preserve-symlinks-flag": "^1.0.0",
|
|
||||||
"svg-parser": "^2.0.4",
|
|
||||||
"svgo": "^1.3.2",
|
|
||||||
"symbol-tree": "^3.2.4",
|
|
||||||
"tailwindcss": "^3.4.19",
|
|
||||||
"tapable": "^2.3.3",
|
|
||||||
"temp-dir": "^2.0.0",
|
|
||||||
"tempy": "^0.6.0",
|
|
||||||
"terminal-link": "^2.1.1",
|
|
||||||
"terser": "^5.47.1",
|
|
||||||
"terser-webpack-plugin": "^5.6.0",
|
|
||||||
"test-exclude": "^6.0.0",
|
|
||||||
"text-table": "^0.2.0",
|
|
||||||
"thenify": "^3.3.1",
|
|
||||||
"thenify-all": "^1.6.0",
|
|
||||||
"throat": "^6.0.2",
|
|
||||||
"thunky": "^1.1.0",
|
|
||||||
"tinyglobby": "^0.2.16",
|
|
||||||
"tmpl": "^1.0.5",
|
|
||||||
"to-regex-range": "^5.0.1",
|
|
||||||
"toidentifier": "^1.0.1",
|
|
||||||
"tough-cookie": "^4.1.4",
|
|
||||||
"tr46": "^2.1.0",
|
|
||||||
"tryer": "^1.0.1",
|
|
||||||
"ts-interface-checker": "^0.1.13",
|
|
||||||
"tsconfig-paths": "^3.15.0",
|
|
||||||
"tslib": "^2.8.1",
|
|
||||||
"tsutils": "^3.21.0",
|
|
||||||
"type-check": "^0.4.0",
|
|
||||||
"type-detect": "^4.0.8",
|
|
||||||
"type-fest": "^0.20.2",
|
|
||||||
"type-is": "^1.6.18",
|
|
||||||
"typed-array-buffer": "^1.0.3",
|
|
||||||
"typed-array-byte-length": "^1.0.3",
|
|
||||||
"typed-array-byte-offset": "^1.0.4",
|
|
||||||
"typed-array-length": "^1.0.7",
|
|
||||||
"typedarray-to-buffer": "^3.1.5",
|
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"unbox-primitive": "^1.1.0",
|
|
||||||
"underscore": "^1.13.6",
|
|
||||||
"undici-types": "^7.24.6",
|
|
||||||
"unicode-canonical-property-names-ecmascript": "^2.0.1",
|
|
||||||
"unicode-match-property-ecmascript": "^2.0.0",
|
|
||||||
"unicode-match-property-value-ecmascript": "^2.2.1",
|
|
||||||
"unicode-property-aliases-ecmascript": "^2.2.0",
|
|
||||||
"unique-string": "^2.0.0",
|
|
||||||
"universalify": "^2.0.1",
|
|
||||||
"unpipe": "^1.0.0",
|
|
||||||
"unquote": "^1.1.1",
|
|
||||||
"upath": "^1.2.0",
|
|
||||||
"update-browserslist-db": "^1.2.3",
|
|
||||||
"uri-js": "^4.4.1",
|
|
||||||
"url-parse": "^1.5.10",
|
|
||||||
"util-deprecate": "^1.0.2",
|
|
||||||
"util.promisify": "^1.0.1",
|
|
||||||
"utila": "^0.4.0",
|
|
||||||
"utils-merge": "^1.0.1",
|
|
||||||
"uuid": "^8.3.2",
|
|
||||||
"v8-to-istanbul": "^8.1.1",
|
|
||||||
"vary": "^1.1.2",
|
|
||||||
"w3c-hr-time": "^1.0.2",
|
|
||||||
"w3c-xmlserializer": "^2.0.0",
|
|
||||||
"walker": "^1.0.8",
|
|
||||||
"watchpack": "^2.5.1",
|
|
||||||
"wbuf": "^1.7.3",
|
|
||||||
"web-vitals": "^2.1.4",
|
|
||||||
"webidl-conversions": "^6.1.0",
|
|
||||||
"webpack": "^5.106.2",
|
|
||||||
"webpack-dev-middleware": "^5.3.4",
|
|
||||||
"webpack-dev-server": "^4.15.2",
|
|
||||||
"webpack-manifest-plugin": "^4.1.1",
|
|
||||||
"webpack-sources": "^3.4.1",
|
|
||||||
"websocket-driver": "^0.7.4",
|
|
||||||
"websocket-extensions": "^0.1.4",
|
|
||||||
"whatwg-encoding": "^1.0.5",
|
|
||||||
"whatwg-fetch": "^3.6.20",
|
|
||||||
"whatwg-mimetype": "^2.3.0",
|
|
||||||
"whatwg-url": "^8.7.0",
|
|
||||||
"which": "^2.0.2",
|
|
||||||
"which-boxed-primitive": "^1.1.1",
|
|
||||||
"which-builtin-type": "^1.2.1",
|
|
||||||
"which-collection": "^1.0.2",
|
|
||||||
"which-typed-array": "^1.1.20",
|
|
||||||
"word-wrap": "^1.2.5",
|
|
||||||
"workbox-background-sync": "^6.6.0",
|
|
||||||
"workbox-broadcast-update": "^6.6.0",
|
|
||||||
"workbox-build": "^6.6.0",
|
|
||||||
"workbox-cacheable-response": "^6.6.0",
|
|
||||||
"workbox-core": "^6.6.0",
|
|
||||||
"workbox-expiration": "^6.6.0",
|
|
||||||
"workbox-google-analytics": "^6.6.0",
|
|
||||||
"workbox-navigation-preload": "^6.6.0",
|
|
||||||
"workbox-precaching": "^6.6.0",
|
|
||||||
"workbox-range-requests": "^6.6.0",
|
|
||||||
"workbox-recipes": "^6.6.0",
|
|
||||||
"workbox-routing": "^6.6.0",
|
|
||||||
"workbox-strategies": "^6.6.0",
|
|
||||||
"workbox-streams": "^6.6.0",
|
|
||||||
"workbox-sw": "^6.6.0",
|
|
||||||
"workbox-webpack-plugin": "^6.6.0",
|
|
||||||
"workbox-window": "^6.6.0",
|
|
||||||
"wrap-ansi": "^7.0.0",
|
|
||||||
"wrappy": "^1.0.2",
|
|
||||||
"write-file-atomic": "^3.0.3",
|
|
||||||
"ws": "^7.5.10",
|
|
||||||
"xml-name-validator": "^3.0.0",
|
|
||||||
"xmlchars": "^2.2.0",
|
|
||||||
"y18n": "^5.0.8",
|
|
||||||
"yallist": "^3.1.1",
|
|
||||||
"yaml": "^1.10.3",
|
|
||||||
"yargs": "^16.2.0",
|
|
||||||
"yargs-parser": "^20.2.9",
|
|
||||||
"yocto-queue": "^0.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"jest": "^27.5.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
},
|
},
|
||||||
"repository": {
|
"eslintConfig": {
|
||||||
"type": "git",
|
"extends": [
|
||||||
"url": "https://code.telepath.ir/blorax/purrgram.git"
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"browserslist": {
|
||||||
"author": "",
|
"production": [
|
||||||
"license": "ISC",
|
">0.2%",
|
||||||
"type": "commonjs"
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+394
@@ -0,0 +1,394 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<title>Static TV Cat Component</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-wrapper {
|
||||||
|
background: #080808;
|
||||||
|
padding: 20px 24px 24px 24px;
|
||||||
|
border-radius: 56px 56px 64px 64px;
|
||||||
|
box-shadow: 0 25px 45px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.05);
|
||||||
|
border: 1px solid #2a2520;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-wrapper::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 20%;
|
||||||
|
width: 60%;
|
||||||
|
height: 8px;
|
||||||
|
background: radial-gradient(ellipse, rgba(70,200,140,0.3), transparent);
|
||||||
|
filter: blur(6px);
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat-artifact {
|
||||||
|
background: black;
|
||||||
|
border-radius: 28px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: inset 0 0 30px rgba(0,0,0,0.7), 0 12px 24px rgba(0,0,0,0.5);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-canvas-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 0 0 3px #3a3530, 0 0 0 7px #0e0e0e;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
filter: url(#heavyStaticFilter) blur(0.6px) contrast(1.3) brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanlines-heavy {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
background: repeating-linear-gradient(0deg,
|
||||||
|
rgba(0,0,0,0.35) 0px,
|
||||||
|
rgba(0,0,0,0.35) 2px,
|
||||||
|
transparent 2px,
|
||||||
|
transparent 5px);
|
||||||
|
z-index: 5;
|
||||||
|
border-radius: 20px;
|
||||||
|
animation: scanlineShift 0.2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scanlineShift {
|
||||||
|
0% { background-position: 0 0; }
|
||||||
|
100% { background-position: 0 4px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.static-overlay-heavy {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"><filter id="tvNoiseHeavy"><feTurbulence type="fractalNoise" baseFrequency="1.2" numOctaves="5" stitchTiles="stitch"><animate attributeName="baseFrequency" values="1.2;1.8;1.2" dur="0.12s" repeatCount="indefinite" /></filter><rect width="100%" height="100%" filter="url(%23tvNoiseHeavy)" opacity="0.7"/></svg>');
|
||||||
|
background-repeat: repeat;
|
||||||
|
background-size: 120px 90px;
|
||||||
|
mix-blend-mode: screen;
|
||||||
|
opacity: 0.65;
|
||||||
|
border-radius: 20px;
|
||||||
|
z-index: 3;
|
||||||
|
animation: staticIntense 0.08s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes staticIntense {
|
||||||
|
0% { background-position: 0% 0%; opacity: 0.6; mix-blend-mode: screen; }
|
||||||
|
25% { background-position: 3% 2%; opacity: 0.75; mix-blend-mode: hard-light; }
|
||||||
|
50% { background-position: -2% 1%; opacity: 0.65; mix-blend-mode: screen; }
|
||||||
|
75% { background-position: 4% 3%; opacity: 0.8; }
|
||||||
|
100% { background-position: 0% 0%; opacity: 0.6; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sparkle-static {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
background-image: radial-gradient(circle at 30% 45%, rgba(255,255,220,0.12) 1px, transparent 1px);
|
||||||
|
background-size: 12px 12px;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
animation: sparkleFlicker 0.1s infinite;
|
||||||
|
z-index: 4;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sparkleFlicker {
|
||||||
|
0% { opacity: 0.4; background-size: 10px 10px; }
|
||||||
|
50% { opacity: 0.7; background-size: 14px 14px; }
|
||||||
|
100% { opacity: 0.4; background-size: 10px 10px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VHS glitch */
|
||||||
|
.glitch-flash {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(255,255,240,0.03);
|
||||||
|
pointer-events: none;
|
||||||
|
animation: vhsFlicker 0.05s infinite;
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes vhsFlicker {
|
||||||
|
0% { opacity: 0.04; background: rgba(255,240,200,0.02); }
|
||||||
|
33% { opacity: 0.12; background: rgba(0,0,0,0.08); }
|
||||||
|
66% { opacity: 0.06; background: rgba(180,210,255,0.03); }
|
||||||
|
100% { opacity: 0.04; background: rgba(0,0,0,0.02); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rolling noise bar */
|
||||||
|
.rolling-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
background: rgba(255,255,240,0.2);
|
||||||
|
filter: blur(3px);
|
||||||
|
animation: rollDown 1.8s linear infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 6;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rollDown {
|
||||||
|
0% { top: -10px; opacity: 0; }
|
||||||
|
10% { opacity: 0.5; }
|
||||||
|
90% { opacity: 0.3; }
|
||||||
|
100% { top: 100%; opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-glow {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -6px;
|
||||||
|
left: 15%;
|
||||||
|
width: 70%;
|
||||||
|
height: 16px;
|
||||||
|
background: radial-gradient(ellipse, rgba(90,220,150,0.25), transparent);
|
||||||
|
filter: blur(10px);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0; padding:0; background:transparent; display:flex; justify-content:center; align-items:center; min-height:100vh;">
|
||||||
|
<div class="crt-wrapper">
|
||||||
|
<div class="cat-artifact">
|
||||||
|
<div class="pixel-canvas-wrapper">
|
||||||
|
<canvas id="pixelCatCanvas" width="416" height="352" style="width:100%; height:auto; max-width:416px; border-radius: 16px;"></canvas>
|
||||||
|
<div class="scanlines-heavy"></div>
|
||||||
|
<div class="static-overlay-heavy"></div>
|
||||||
|
<div class="sparkle-static"></div>
|
||||||
|
<div class="glitch-flash"></div>
|
||||||
|
<div class="rolling-bar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="crt-glow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SVG STATIC FILTER -->
|
||||||
|
<svg style="position: absolute; width: 0; height: 0; overflow: visible;" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<filter id="heavyStaticFilter" x="-15%" y="-15%" width="130%" height="130%">
|
||||||
|
<feGaussianBlur in="SourceGraphic" stdDeviation="0.55" result="bloom" />
|
||||||
|
<feComponentTransfer in="bloom" result="boostContrast">
|
||||||
|
<feFuncR type="linear" slope="1.45" intercept="-0.2"/>
|
||||||
|
<feFuncG type="linear" slope="1.45" intercept="-0.2"/>
|
||||||
|
<feFuncB type="linear" slope="1.45" intercept="-0.2"/>
|
||||||
|
<feFuncA type="linear" slope="1" intercept="0"/>
|
||||||
|
</feComponentTransfer>
|
||||||
|
<feTurbulence type="fractalNoise" baseFrequency="2.2" numOctaves="4" result="intenseNoise">
|
||||||
|
<animate attributeName="baseFrequency" values="2.1;2.5;2.1" dur="0.08s" repeatCount="indefinite" />
|
||||||
|
</feTurbulence>
|
||||||
|
<feDisplacementMap in="boostContrast" in2="intenseNoise" scale="1.8" xChannelSelector="R" yChannelSelector="G" result="displacedStatic"/>
|
||||||
|
<feColorMatrix type="matrix" values="1.1 0 0 0 0.05 0 1.05 0 0 0.02 0 0 1.1 0 0.03 0 0 0 1 0" in="displacedStatic" result="colorNoise"/>
|
||||||
|
<feGaussianBlur in="colorNoise" stdDeviation="0.3" result="finalStatic"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
// CAT PIXEL DATA
|
||||||
|
const originalCatRows = [
|
||||||
|
[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],
|
||||||
|
[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
|
||||||
|
[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
|
||||||
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
||||||
|
[0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
|
||||||
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0],
|
||||||
|
[0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],
|
||||||
|
[0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0],
|
||||||
|
[0,0,0,0,0,0,1,1,1,1,0,0,1,1,0,1,1,1,0,0,0,0,0,0,0,0],
|
||||||
|
[0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
|
||||||
|
[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0],
|
||||||
|
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentCatRows = JSON.parse(JSON.stringify(originalCatRows));
|
||||||
|
const rows = 22, cols = 26;
|
||||||
|
const cellSize = 16;
|
||||||
|
const canvas = document.getElementById('pixelCatCanvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
const leftEyeCols = [6, 7, 8, 9];
|
||||||
|
const rightEyeCols = [15, 16, 17, 18];
|
||||||
|
const eyeRow = 12;
|
||||||
|
|
||||||
|
let leftPupilCol = 7;
|
||||||
|
let rightPupilCol = 16;
|
||||||
|
let isBlinking = false;
|
||||||
|
|
||||||
|
function updatePupilInMatrix() {
|
||||||
|
for (let col of leftEyeCols) {
|
||||||
|
if (currentCatRows[eyeRow][col] === 1) currentCatRows[eyeRow][col] = 0;
|
||||||
|
}
|
||||||
|
for (let col of rightEyeCols) {
|
||||||
|
if (currentCatRows[eyeRow][col] === 1) currentCatRows[eyeRow][col] = 0;
|
||||||
|
}
|
||||||
|
if (leftEyeCols.includes(leftPupilCol)) currentCatRows[eyeRow][leftPupilCol] = 1;
|
||||||
|
else currentCatRows[eyeRow][7] = 1;
|
||||||
|
if (rightEyeCols.includes(rightPupilCol)) currentCatRows[eyeRow][rightPupilCol] = 1;
|
||||||
|
else currentCatRows[eyeRow][16] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCatFromMatrix() {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
for (let row = 0; row < rows; row++) {
|
||||||
|
for (let col = 0; col < cols; col++) {
|
||||||
|
const val = currentCatRows[row][col];
|
||||||
|
const x = col * cellSize;
|
||||||
|
const y = row * cellSize;
|
||||||
|
|
||||||
|
if (val === 1) {
|
||||||
|
ctx.fillStyle = "#12100c";
|
||||||
|
ctx.fillRect(x, y, cellSize, cellSize);
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
const dx = x + 2 + Math.random() * (cellSize - 4);
|
||||||
|
const dy = y + 2 + Math.random() * (cellSize - 4);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(dx, dy, 0.7 + Math.random() * 1.7, 0, Math.PI*2);
|
||||||
|
ctx.fillStyle = `rgba(35,30,24,${0.5 + Math.random() * 0.5})`;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
if (Math.random() > 0.78) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x + Math.random() * cellSize, y + Math.random() * cellSize, 1.1, 0, Math.PI*2);
|
||||||
|
ctx.fillStyle = `rgba(210,200,180,0.6)`;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = "#f5f2ea";
|
||||||
|
ctx.fillRect(x, y, cellSize, cellSize);
|
||||||
|
for (let d = 0; d < 4; d++) {
|
||||||
|
ctx.fillStyle = `rgba(0,0,0,0.07)`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x + Math.random() * cellSize, y + Math.random() * cellSize, 0.9, 0, Math.PI*2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
if (Math.random() > 0.92) {
|
||||||
|
ctx.fillStyle = `rgba(30,25,20,0.4)`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x + Math.random() * cellSize, y + Math.random() * cellSize, 1.3, 0, Math.PI*2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 1200; i++) {
|
||||||
|
const randX = Math.random() * canvas.width;
|
||||||
|
const randY = Math.random() * canvas.height;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(randX, randY, 0.6 + Math.random() * 1.8, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = `rgba(0,0,0,${0.1 + Math.random() * 0.35})`;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
for (let s = 0; s < 400; s++) {
|
||||||
|
const sx = Math.random() * canvas.width;
|
||||||
|
const sy = Math.random() * canvas.height;
|
||||||
|
ctx.fillStyle = `rgba(230,220,190,${0.2 + Math.random() * 0.5})`;
|
||||||
|
ctx.fillRect(sx, sy, 1.5 + Math.random() * 3, 1);
|
||||||
|
}
|
||||||
|
for (let b = 0; b < 45; b++) {
|
||||||
|
const bandY = Math.random() * canvas.height;
|
||||||
|
ctx.fillStyle = `rgba(0,0,0,0.12)`;
|
||||||
|
ctx.fillRect(0, bandY, canvas.width, 1.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function performBlink() {
|
||||||
|
if (isBlinking) return;
|
||||||
|
isBlinking = true;
|
||||||
|
const savedLeft = leftPupilCol;
|
||||||
|
const savedRight = rightPupilCol;
|
||||||
|
for (let col of leftEyeCols) currentCatRows[eyeRow][col] = 1;
|
||||||
|
for (let col of rightEyeCols) currentCatRows[eyeRow][col] = 1;
|
||||||
|
drawCatFromMatrix();
|
||||||
|
setTimeout(() => {
|
||||||
|
for (let col of leftEyeCols) currentCatRows[eyeRow][col] = 0;
|
||||||
|
for (let col of rightEyeCols) currentCatRows[eyeRow][col] = 0;
|
||||||
|
leftPupilCol = savedLeft;
|
||||||
|
rightPupilCol = savedRight;
|
||||||
|
updatePupilInMatrix();
|
||||||
|
drawCatFromMatrix();
|
||||||
|
isBlinking = false;
|
||||||
|
}, 130);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (isBlinking) return;
|
||||||
|
leftPupilCol = (leftPupilCol === 7) ? 8 : 7;
|
||||||
|
rightPupilCol = (rightPupilCol === 16) ? 17 : 16;
|
||||||
|
updatePupilInMatrix();
|
||||||
|
drawCatFromMatrix();
|
||||||
|
}, 480);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (!isBlinking) performBlink();
|
||||||
|
}, 2600 + Math.random() * 2400);
|
||||||
|
|
||||||
|
const wrapper = document.querySelector('.pixel-canvas-wrapper');
|
||||||
|
wrapper.addEventListener('mouseenter', () => { if (!isBlinking) performBlink(); });
|
||||||
|
wrapper.addEventListener('click', () => {
|
||||||
|
if (!isBlinking) performBlink();
|
||||||
|
setTimeout(() => { if (!isBlinking) performBlink(); }, 180);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let col of leftEyeCols) currentCatRows[eyeRow][col] = 0;
|
||||||
|
for (let col of rightEyeCols) currentCatRows[eyeRow][col] = 0;
|
||||||
|
leftPupilCol = 7;
|
||||||
|
rightPupilCol = 16;
|
||||||
|
updatePupilInMatrix();
|
||||||
|
drawCatFromMatrix();
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (!isBlinking) drawCatFromMatrix();
|
||||||
|
}, 180);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<title>Purrgram Login</title>
|
||||||
|
<!-- Pixel font -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import CRTTerminal from './components/CRT/CRTTerminal';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<CRTTerminal />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,456 @@
|
|||||||
|
/* CRT Terminal Styles */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base body styles - applied to container */
|
||||||
|
.crt-terminal {
|
||||||
|
background: #0a0a0a;
|
||||||
|
min-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: 'Press Start 2P', 'Courier New', monospace;
|
||||||
|
padding: 20px;
|
||||||
|
transition: background 0.1s linear;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CRT static grain overlay */
|
||||||
|
.crt-terminal::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: repeating-linear-gradient(0deg, rgba(0,0,0,0.06) 0px, rgba(0,0,0,0.06) 1px, transparent 1px, transparent 4px);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LIGHT MODE */
|
||||||
|
.crt-terminal.light-mode {
|
||||||
|
background: #e0dcd0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide iframe scrollbars */
|
||||||
|
.cat-iframe {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat-iframe::-webkit-scrollbar {
|
||||||
|
display: none; /* Hides scrollbar in Chrome/Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .pixel-card {
|
||||||
|
background: #f0ece0;
|
||||||
|
border-color: #2a2a2a;
|
||||||
|
box-shadow: 8px 8px 0 #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .pixel-input {
|
||||||
|
background: #ffffff;
|
||||||
|
border-color: #4a4a4a;
|
||||||
|
color: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .pixel-input:focus {
|
||||||
|
border-color: #000000;
|
||||||
|
box-shadow: 0 0 0 2px #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .pixel-btn {
|
||||||
|
background: #d0ccc0;
|
||||||
|
border-color: #3a3a3a;
|
||||||
|
color: #111111;
|
||||||
|
box-shadow: inset 0 1px 0 #ffffff, 3px 3px 0 #4a4a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .pixel-btn:active {
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
box-shadow: inset 0 1px 0 #ffffff, 1px 1px 0 #4a4a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .toggle-pixel {
|
||||||
|
background: #c8c4b8;
|
||||||
|
border-color: #4a4a4a;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .toggle-pixel.active {
|
||||||
|
background: #2a2a2a;
|
||||||
|
border-color: #e0e0e0;
|
||||||
|
color: #f0f0f0;
|
||||||
|
box-shadow: inset 0 0 0 1px #5a5a5a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .side-block {
|
||||||
|
border-color: #5a5a5a;
|
||||||
|
background: #d8d4c8;
|
||||||
|
color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode .theme-pixel {
|
||||||
|
background: #c8c4b8;
|
||||||
|
border-color: #3a3a3a;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crt-terminal.light-mode::before {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAIN GRID */
|
||||||
|
.pixel-grid {
|
||||||
|
max-width: 1260px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 28px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
min-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SIDE PANELS */
|
||||||
|
.side-pixel {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 100px;
|
||||||
|
max-width: 180px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-block {
|
||||||
|
width: 100%;
|
||||||
|
height: 280px;
|
||||||
|
border: 3px solid #5a5a5a;
|
||||||
|
background: #1a1a1a;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 9px;
|
||||||
|
text-align: center;
|
||||||
|
color: #888888;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
gap: 14px;
|
||||||
|
box-shadow: inset 0 0 0 2px #2a2a2a, 6px 6px 0 #000000;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-block span {
|
||||||
|
display: block;
|
||||||
|
font-size: 20px;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CENTER CARD */
|
||||||
|
.pixel-card {
|
||||||
|
background: #121212;
|
||||||
|
border: 3px solid #5a5a5a;
|
||||||
|
padding: 20px 24px 28px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 490px;
|
||||||
|
box-shadow: 12px 12px 0 #050505;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CRT scanline effect */
|
||||||
|
.pixel-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.15) 0px, rgba(0, 0, 0, 0.15) 2px, transparent 2px, transparent 6px);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Static noise */
|
||||||
|
.pixel-card::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: radial-gradient(circle at 20% 40%, rgba(100, 100, 100, 0.1) 1px, transparent 1px);
|
||||||
|
background-size: 6px 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CAT IFRAME CONTAINER */
|
||||||
|
.cat-zone {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
border: 2px solid #4a4a4a;
|
||||||
|
background: #000000;
|
||||||
|
box-shadow: inset 0 0 0 2px #2a2a2a, 4px 4px 0 #00000066;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat-iframe {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-width: 352px;
|
||||||
|
aspect-ratio: 416 / 352;
|
||||||
|
border: none;
|
||||||
|
background: black;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle group */
|
||||||
|
.toggle-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-pixel {
|
||||||
|
font-family: 'Press Start 2P', monospace;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 2px solid #6a6a6a;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 8px 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #e0e0e0;
|
||||||
|
transition: all 0.03s linear;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
box-shadow: inset 0 1px 0 #4a4a4a, 2px 2px 0 #030303;
|
||||||
|
border-radius: 0px;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-pixel.active {
|
||||||
|
background: #e0e0e0;
|
||||||
|
border-color: #2a2a2a;
|
||||||
|
color: #0a0a0a;
|
||||||
|
box-shadow: inset 0 0 0 1px #ffffff70, 1px 1px 0 #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form elements */
|
||||||
|
.input-pixel-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 9px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #aaaaaa;
|
||||||
|
text-transform: uppercase;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-input {
|
||||||
|
width: 100%;
|
||||||
|
background: #0a0a0a;
|
||||||
|
border: 2px solid #5a5a5a;
|
||||||
|
font-family: 'Press Start 2P', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
color: #eeeeee;
|
||||||
|
outline: none;
|
||||||
|
transition: 0.05s linear;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-input:focus {
|
||||||
|
border-color: #aaaaaa;
|
||||||
|
background: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-input::placeholder {
|
||||||
|
color: #555555;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.pixel-btn {
|
||||||
|
width: 100%;
|
||||||
|
background: #2a2a2a;
|
||||||
|
border: none;
|
||||||
|
font-family: 'Press Start 2P', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 12px 12px;
|
||||||
|
margin-top: 12px;
|
||||||
|
color: #eeeeee;
|
||||||
|
border-bottom: 3px solid #6a6a6a;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: 0.03s linear;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 0px;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-btn:active {
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google-pixel {
|
||||||
|
width: 100%;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 2px solid #6a6a6a;
|
||||||
|
font-family: 'Press Start 2P', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 18px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #cccccc;
|
||||||
|
transition: 0.05s linear;
|
||||||
|
border-radius: 0px;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.google-pixel:active {
|
||||||
|
background: #2a2a2a;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* POPUP NOTIFICATION */
|
||||||
|
.popup-notification {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: #0a0a0a;
|
||||||
|
border: 3px solid #aa2222;
|
||||||
|
padding: 20px 28px;
|
||||||
|
z-index: 10000;
|
||||||
|
font-family: 'Press Start 2P', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 380px;
|
||||||
|
width: 90%;
|
||||||
|
box-shadow: 12px 12px 0 rgba(0,0,0,0.6);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: #eeeeee;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-notification.error {
|
||||||
|
border-color: #cc3333;
|
||||||
|
background: #1a0a0a;
|
||||||
|
color: #ff8888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-notification.success {
|
||||||
|
border-color: #888888;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-notification .popup-message {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-button {
|
||||||
|
background: #2a2a2a;
|
||||||
|
border: 2px solid #6a6a6a;
|
||||||
|
font-family: 'Press Start 2P', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
padding: 8px 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #dddddd;
|
||||||
|
transition: 0.03s linear;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 8px;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-button:active {
|
||||||
|
transform: translate(1px, 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Theme toggle button */
|
||||||
|
.theme-pixel {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 2px solid #6a6a6a;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 999;
|
||||||
|
transition: 0.05s linear;
|
||||||
|
box-shadow: 4px 4px 0 #00000044;
|
||||||
|
border-radius: 0px;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-pixel:active {
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utility - ensure no rounded corners */
|
||||||
|
.pixel-card, .pixel-btn, .google-pixel, .toggle-pixel,
|
||||||
|
.pixel-input, .side-block, .cat-zone, .cat-iframe,
|
||||||
|
.theme-pixel, .popup-notification {
|
||||||
|
border-radius: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 860px) {
|
||||||
|
.side-pixel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.pixel-grid {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 540px) {
|
||||||
|
.crt-terminal {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.pixel-card {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.toggle-pixel {
|
||||||
|
font-size: 8px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
.pixel-input {
|
||||||
|
font-size: 9px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.popup-notification {
|
||||||
|
font-size: 9px;
|
||||||
|
padding: 16px;
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import LoginForm from './LoginForm';
|
||||||
|
import SignupForm from './SignupForm';
|
||||||
|
import PopupNotification from './PopupNotification';
|
||||||
|
import ThemeToggle from './ThemeToggle';
|
||||||
|
import { usePopup } from '../../hooks/usePopup';
|
||||||
|
import './CRTTerminal.css';
|
||||||
|
|
||||||
|
const CRTTerminal = () => {
|
||||||
|
const [mode, setMode] = useState('login'); // 'login' or 'signup'
|
||||||
|
const [isLightMode, setIsLightMode] = useState(false);
|
||||||
|
const { popup, showPopup, hidePopup } = usePopup();
|
||||||
|
const iframeRef = useRef(null);
|
||||||
|
|
||||||
|
// Theme management
|
||||||
|
useEffect(() => {
|
||||||
|
const stored = localStorage.getItem('crtPixelTheme');
|
||||||
|
if (stored === 'light') {
|
||||||
|
setIsLightMode(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLightMode) {
|
||||||
|
document.body.classList.add('light-mode');
|
||||||
|
localStorage.setItem('crtPixelTheme', 'light');
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('light-mode');
|
||||||
|
localStorage.setItem('crtPixelTheme', 'dark');
|
||||||
|
}
|
||||||
|
}, [isLightMode]);
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
setIsLightMode(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iframe scaling
|
||||||
|
useEffect(() => {
|
||||||
|
const adjustIframe = () => {
|
||||||
|
if (iframeRef.current) {
|
||||||
|
const container = iframeRef.current.parentElement;
|
||||||
|
if (container) {
|
||||||
|
const containerWidth = container.clientWidth;
|
||||||
|
iframeRef.current.style.width = containerWidth < 352 ? '100%' : '352px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', adjustIframe);
|
||||||
|
adjustIframe();
|
||||||
|
|
||||||
|
return () => window.removeEventListener('resize', adjustIframe);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Auth handlers
|
||||||
|
const handleLogin = (email, password) => {
|
||||||
|
// Demo login validation
|
||||||
|
if (password.toLowerCase() === 'wrong' || email === 'denied@error.com') {
|
||||||
|
showPopup('ACCESS DENIED: INVALID CREDENTIALS', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usernamePart = email.split('@')[0];
|
||||||
|
showPopup(`ACCESS GRANTED: WELCOME ${usernamePart}`, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSignup = (userData) => {
|
||||||
|
showPopup(`ACCOUNT CREATED: USERNAME [${userData.username}] EMAIL [${userData.email}]`, false);
|
||||||
|
// Switch to login mode after successful signup
|
||||||
|
setMode('login');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGoogleAuth = () => {
|
||||||
|
showPopup('GOOGLE AUTHENTICATION: DEMO FLOW CONNECTED', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`crt-terminal ${isLightMode ? 'light-mode' : ''}`}>
|
||||||
|
<div className="pixel-grid">
|
||||||
|
{/* LEFT empty zone */}
|
||||||
|
<div className="side-pixel">
|
||||||
|
<div className="side-block">
|
||||||
|
<span>◢◤</span>
|
||||||
|
[ 8-BIT ZONE ]
|
||||||
|
<span>◥◣</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* MAIN CARD */}
|
||||||
|
<div className="pixel-card">
|
||||||
|
{/* CAT IFRAME */}
|
||||||
|
<div className="cat-zone">
|
||||||
|
<iframe
|
||||||
|
ref={iframeRef}
|
||||||
|
className="cat-iframe"
|
||||||
|
src="/cat.html"
|
||||||
|
title="CRT PIXEL CAT"
|
||||||
|
width="352"
|
||||||
|
height="298"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '352px',
|
||||||
|
aspectRatio: '416/352',
|
||||||
|
background: '#000'
|
||||||
|
}}
|
||||||
|
loading="eager"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TOGGLE LOGIN / SIGNUP */}
|
||||||
|
<div className="toggle-row">
|
||||||
|
<button
|
||||||
|
className={`toggle-pixel ${mode === 'login' ? 'active' : ''}`}
|
||||||
|
onClick={() => setMode('login')}
|
||||||
|
>
|
||||||
|
LOGIN
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`toggle-pixel ${mode === 'signup' ? 'active' : ''}`}
|
||||||
|
onClick={() => setMode('signup')}
|
||||||
|
>
|
||||||
|
SIGN UP
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DYNAMIC FORM */}
|
||||||
|
{mode === 'login' ? (
|
||||||
|
<LoginForm onLogin={handleLogin} showPopup={showPopup} />
|
||||||
|
) : (
|
||||||
|
<SignupForm onSignup={handleSignup} showPopup={showPopup} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button className="google-pixel" onClick={handleGoogleAuth}>
|
||||||
|
<span>◉</span> SIGN WITH GOOGLE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* RIGHT empty zone */}
|
||||||
|
<div className="side-pixel">
|
||||||
|
<div className="side-block">
|
||||||
|
<span>◣◢</span>
|
||||||
|
[ PIXEL SLOT ]
|
||||||
|
<span>▣</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ThemeToggle isLightMode={isLightMode} onToggle={toggleTheme} />
|
||||||
|
<PopupNotification
|
||||||
|
visible={popup.visible}
|
||||||
|
message={popup.message}
|
||||||
|
isError={popup.isError}
|
||||||
|
onClose={hidePopup}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CRTTerminal;
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { validateLogin } from '../../utils/validation';
|
||||||
|
|
||||||
|
const LoginForm = ({ onLogin, showPopup }) => {
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const { email, password } = formData;
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
[e.target.name]: e.target.value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const error = validateLogin(email, password);
|
||||||
|
if (error) {
|
||||||
|
showPopup(`ERROR: ${error}`, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogin(email, password);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="input-pixel-group">
|
||||||
|
<label className="pixel-label" htmlFor="login-email"> EMAIL</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="login-email"
|
||||||
|
name="email"
|
||||||
|
value={email}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="pixel-input"
|
||||||
|
placeholder="user@crt.city"
|
||||||
|
autoComplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="input-pixel-group">
|
||||||
|
<label className="pixel-label" htmlFor="login-password"> PASSWORD</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="login-password"
|
||||||
|
name="password"
|
||||||
|
value={password}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="pixel-input"
|
||||||
|
placeholder="********"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" className="pixel-btn">
|
||||||
|
[ LOGIN ]
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoginForm;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const PopupNotification = ({ visible, message, isError, onClose }) => {
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`popup-notification ${isError ? 'error' : 'success'}`}>
|
||||||
|
<div className="popup-message">{message}</div>
|
||||||
|
<button className="popup-button" onClick={onClose}>
|
||||||
|
[ OK ]
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PopupNotification;
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { validateSignup } from '../../utils/validation';
|
||||||
|
|
||||||
|
const SignupForm = ({ onSignup, showPopup }) => {
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
email: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
confirmPassword: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const { email, username, password, confirmPassword } = formData;
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
[e.target.name]: e.target.value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const error = validateSignup(email, password, username, confirmPassword);
|
||||||
|
if (error) {
|
||||||
|
showPopup(`ERROR: ${error}`, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSignup({ email, username, password });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="input-pixel-group">
|
||||||
|
<label className="pixel-label" htmlFor="signup-email"> EMAIL</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="signup-email"
|
||||||
|
name="email"
|
||||||
|
value={email}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="pixel-input"
|
||||||
|
placeholder="user@crt.city"
|
||||||
|
autoComplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="input-pixel-group">
|
||||||
|
<label className="pixel-label" htmlFor="signup-username"> USERNAME</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="signup-username"
|
||||||
|
name="username"
|
||||||
|
value={username}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="pixel-input"
|
||||||
|
placeholder="pixel.cat"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="input-pixel-group">
|
||||||
|
<label className="pixel-label" htmlFor="signup-password"> PASSWORD</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="signup-password"
|
||||||
|
name="password"
|
||||||
|
value={password}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="pixel-input"
|
||||||
|
placeholder="********"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="input-pixel-group">
|
||||||
|
<label className="pixel-label" htmlFor="signup-confirm"> CONFIRM PASSWORD</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="signup-confirm"
|
||||||
|
name="confirmPassword"
|
||||||
|
value={confirmPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="pixel-input"
|
||||||
|
placeholder="match the key"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" className="pixel-btn">
|
||||||
|
[ REGISTER ]
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignupForm;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ThemeToggle = ({ isLightMode, onToggle }) => {
|
||||||
|
return (
|
||||||
|
<div className="theme-pixel" onClick={onToggle} aria-label="Toggle theme">
|
||||||
|
●○
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeToggle;
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
const { Pool } = require('pg');
|
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
const pool = new Pool({
|
|
||||||
connectionString: process.env.DATABASE_URL,
|
|
||||||
});
|
|
||||||
|
|
||||||
// A quick helper to log queries (super helpful for learning and debugging!)
|
|
||||||
module.exports = {
|
|
||||||
query: (text, params) => {
|
|
||||||
console.log('Executing query:', text);
|
|
||||||
return pool.query(text, params);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
|
export const usePopup = () => {
|
||||||
|
const [popup, setPopup] = useState({ visible: false, message: '', isError: true });
|
||||||
|
|
||||||
|
const showPopup = useCallback((message, isError = true) => {
|
||||||
|
setPopup({ visible: true, message: message || (isError ? 'ERROR' : 'SUCCESS'), isError });
|
||||||
|
|
||||||
|
// Auto-hide after 4.2 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
setPopup(prev => ({ ...prev, visible: false }));
|
||||||
|
}, 4200);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const hidePopup = useCallback(() => {
|
||||||
|
setPopup(prev => ({ ...prev, visible: false }));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { popup, showPopup, hidePopup };
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/* Base reset to match original exactly */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #0a0a0a; /* Match original body background */
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
+10
-33
@@ -1,34 +1,11 @@
|
|||||||
const express = require('express');
|
import React from 'react';
|
||||||
const db = require('./config/db');
|
import ReactDOM from 'react-dom/client';
|
||||||
require('dotenv').config();
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
const app = express();
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
const PORT = process.env.PORT || 3000;
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
// Middleware to let Express understand incoming JSON data
|
<App />
|
||||||
app.use(express.json());
|
</React.StrictMode>
|
||||||
|
);
|
||||||
// A simple sanity-check route
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
res.json({ message: 'Welcome to the Purrgram API!' });
|
|
||||||
});
|
|
||||||
|
|
||||||
// A test route to see if we can fetch the courses from PostgreSQL
|
|
||||||
app.get('/test-db', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const result = await db.query('SELECT * FROM courses;');
|
|
||||||
res.json({
|
|
||||||
status: 'success',
|
|
||||||
message: 'Connected to database successfully!',
|
|
||||||
data: result.rows,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.status(500).json({ status: 'error', message: 'Database connection failed' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start the server
|
|
||||||
app.listen(PORT, () => {
|
|
||||||
console.log(`Server is running smoothly on http://localhost:${PORT}`);
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
export const ValidationRules = {
|
||||||
|
email: (email) => {
|
||||||
|
if (!email) return 'EMAIL REQUIRED';
|
||||||
|
if (!email.includes('@') || !email.includes('.')) return 'ENTER VALID EMAIL ADDRESS';
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
password: (password) => {
|
||||||
|
if (!password) return 'PASSWORD CANNOT BE EMPTY';
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
username: (username) => {
|
||||||
|
if (!username) return 'USERNAME IS REQUIRED';
|
||||||
|
if (username.length < 3) return 'USERNAME MUST BE AT LEAST 3 CHARACTERS';
|
||||||
|
if (!/^[a-zA-Z0-9_.-]+$/.test(username)) {
|
||||||
|
return 'USERNAME CAN ONLY CONTAIN LETTERS, NUMBERS, _ . -';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmPassword: (password, confirmPassword) => {
|
||||||
|
if (password !== confirmPassword) return 'PASSWORDS DO NOT MATCH';
|
||||||
|
if (password.length < 4) return 'PASSWORD MUST BE AT LEAST 4 CHARACTERS';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateLogin = (email, password) => {
|
||||||
|
const emailError = ValidationRules.email(email);
|
||||||
|
if (emailError) return emailError;
|
||||||
|
|
||||||
|
const passwordError = ValidationRules.password(password);
|
||||||
|
if (passwordError) return passwordError;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateSignup = (email, password, username, confirmPassword) => {
|
||||||
|
const emailError = ValidationRules.email(email);
|
||||||
|
if (emailError) return emailError;
|
||||||
|
|
||||||
|
const usernameError = ValidationRules.username(username);
|
||||||
|
if (usernameError) return usernameError;
|
||||||
|
|
||||||
|
const passwordError = ValidationRules.password(password);
|
||||||
|
if (passwordError) return passwordError;
|
||||||
|
|
||||||
|
const confirmError = ValidationRules.confirmPassword(password, confirmPassword);
|
||||||
|
if (confirmError) return confirmError;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user