diff --git a/.env b/.env deleted file mode 100644 index 938fb696..00000000 --- a/.env +++ /dev/null @@ -1,26 +0,0 @@ -SKIP_PREFLIGHT_CHECK=true -REACT_APP_MODE=play -REACT_APP_authToken="" -REACT_APP_PID=learner-ai-story-demo -REACT_APP_UID=anonymous -REACT_APP_ID=all.ll.app -REACT_APP_VER=0.3.0 -REACT_APP_TIMEDIFF=0 -REACT_APP_HOST=https://telemetry-dev.theall.ai -REACT_APP_ENDPOINT=telemetry -REACT_APP_APISLUG=/v1/ -REACT_APP_CHANNEL=learner-ai-demo -REACT_APP_ENV=all-player -REACT_APP_BATCHSIZE=1 -REACT_APP_CONTENT_SIZE=5 -REACT_APP_LANGUAGE=ta -REACT_APP_TELEMETRY_MODE=DT -REACT_APP_LEARNER_AI_APP_HOST=https://all-learnerai-tn-dev.theall.ai -REACT_APP_LEARNER_AI_ORCHESTRATION_HOST=https://telemetry-dev.theall.ai/all-orchestration-services -REACT_APP_CAPTURE_AUDIO=true -REACT_APP_AWS_S3_BUCKET_NAME=all-dev-storage -REACT_APP_AWS_S3_BUCKET_URL=https://all-dev-storage.s3.ap-south-1.amazonaws.com -REACT_APP_AWS_S3_REGION=ap-south-1 -REACT_APP_AWS_S3_BUCKET_CONTENT_URL=https://all-dev-content-service.s3.ap-south-1.amazonaws.com -REACT_APP_MIN_DECIBELS=-35 -REACT_APP_IS_AUDIOPREPROCESSING=true \ No newline at end of file diff --git a/.github/workflows/all-dev-rig.yml b/.github/workflows/all-dev-rig.yml index ee992317..b69e6bcc 100644 --- a/.github/workflows/all-dev-rig.yml +++ b/.github/workflows/all-dev-rig.yml @@ -1,9 +1,9 @@ -name: ALL rig dev Deployment +name: ALL rig (UAT) dev Deployment on: push: branches: - - all-2.5.0 + - all-3.0.2 jobs: deploy: @@ -38,6 +38,7 @@ jobs: - name: Build and Package Application env: + NODE_OPTIONS: "--max_old_space_size=4096" SKIP_PREFLIGHT_CHECK: ${{ vars.SKIP_PREFLIGHT_CHECK }} REACT_APP_MODE: ${{ vars.REACT_APP_MODE }} REACT_APP_authToken: ${{ vars.REACT_APP_authToken }} @@ -78,6 +79,7 @@ jobs: REACT_APP_MAX_LEVEL: ${{ vars.REACT_APP_MAX_LEVEL }} REACT_APP_LEARNER_AI_BASE_URL: ${{ vars.REACT_APP_LEARNER_AI_BASE_URL }} REACT_APP_USE_RECOMMENDATION_API: ${{ vars.REACT_APP_USE_RECOMMENDATION_API }} + REACT_APP_ENGAGEMENT_PREDICT_URL: ${{ vars.REACT_APP_ENGAGEMENT_PREDICT_URL }} CI: false # Disabling CI to not treat warnings as errors run: npm run build diff --git a/.gitignore b/.gitignore index adce5943..696b1d13 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,5 @@ dist .tern-port build +.env.local +.env \ No newline at end of file diff --git a/config-overrides.js b/config-overrides.js new file mode 100644 index 00000000..1b372131 --- /dev/null +++ b/config-overrides.js @@ -0,0 +1,25 @@ +// Webpack configuration override for Create React App +// This allows us to import TypeScript files from the library directory +const { override } = require('customize-cra'); + +module.exports = override( + (config) => { + // Ensure TypeScript files are resolved + if (!config.resolve) { + config.resolve = {}; + } + if (!config.resolve.extensions) { + config.resolve.extensions = []; + } + // Add .ts and .tsx if not already present (before .js for priority) + const extensions = config.resolve.extensions; + if (!extensions.includes('.ts')) { + extensions.unshift('.ts'); + } + if (!extensions.includes('.tsx')) { + extensions.unshift('.tsx'); + } + return config; + } +); + diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d921509a..00000000 --- a/package-lock.json +++ /dev/null @@ -1,30354 +0,0 @@ -{ - "name": "new", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "new", - "version": "0.1.0", - "dependencies": { - "@aws-sdk/client-s3": "^3.388.0", - "@emotion/react": "^11.7.0", - "@emotion/styled": "^11.6.0", - "@fingerprintjs/fingerprintjs": "^4.2.2", - "@mui/icons-material": "^5.2.5", - "@mui/material": "^5.2.2", - "@mui/styles": "^5.2.3", - "@number-flow/react": "^0.5.10", - "@project-sunbird/client-services": "^5.1.2", - "@reduxjs/toolkit": "^2.2.0", - "@tekdi/all-telemetry-sdk": "^0.0.32", - "@tekdi/multilingual-profanity-filter": "1.1.0", - "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", - "@xenova/transformers": "^2.1.0", - "ajv": "^8.17.1", - "axios": "^1.9.0", - "canvas-confetti": "^1.9.2", - "character-error-rate": "^1.1.4", - "classnames": "^2.3.1", - "crypto-js": "^4.2.0", - "double-metaphone": "^2.0.1", - "eslint-plugin-import": "^2.28.0", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-react": "^7.33.1", - "faker": "^5.5.3", - "framer-motion": "^12.4.1", - "homophones": "^1.0.1", - "jwt-decode": "^4.0.0", - "lodash": "^4.17.21", - "memoize-one": "^6.0.0", - "metaphone": "^2.0.1", - "react": "^18.2.0", - "react-audio-analyser": "^1.0.0", - "react-audio-player": "^0.17.0", - "react-confetti": "^6.1.0", - "react-dom": "^18.2.0", - "react-infinite-scroll-component": "^6.1.0", - "react-joyride": "^2.9.3", - "react-redux": "^9.1.0", - "react-router-dom": "^6.0.2", - "react-scripts": "5.0.1", - "react-speech-recognition": "^3.10.0", - "react-virtualized": "^9.22.3", - "react-virtualized-auto-sizer": "^1.0.6", - "react-window": "^1.8.6", - "recordrtc": "^5.6.2", - "redux": "^4.1.2", - "redux-saga": "^1.1.3", - "sass": "^1.44.0", - "split-graphemes": "^0.5.0", - "use-sound": "^4.0.1", - "web-vitals": "^2.1.4" - }, - "devDependencies": { - "@mui/styles": "^5.15.10", - "eslint": "^7.32.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^3.4.0", - "husky": "^9.0.11", - "lint-staged": "^11.0.0", - "prettier": "^2.3.2", - "react": "^18.2.0" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@adobe/css-tools": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", - "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", - "license": "MIT" - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@apideck/better-ajv-errors": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", - "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", - "license": "MIT", - "dependencies": { - "json-schema": "^0.4.0", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "ajv": ">=8" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.674.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.674.0.tgz", - "integrity": "sha512-/pbAGFXUFqhZPrb4vACYdJzAc/lX/MPW1sxRoiX46BVjvpT7QkYwRjlzuifskLyDf2TjkGNsXu+F6AgPGCXFUg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.670.0", - "@aws-sdk/client-sts": "3.670.0", - "@aws-sdk/core": "3.667.0", - "@aws-sdk/credential-provider-node": "3.670.0", - "@aws-sdk/middleware-bucket-endpoint": "3.667.0", - "@aws-sdk/middleware-expect-continue": "3.667.0", - "@aws-sdk/middleware-flexible-checksums": "3.669.0", - "@aws-sdk/middleware-host-header": "3.667.0", - "@aws-sdk/middleware-location-constraint": "3.667.0", - "@aws-sdk/middleware-logger": "3.667.0", - "@aws-sdk/middleware-recursion-detection": "3.667.0", - "@aws-sdk/middleware-sdk-s3": "3.674.0", - "@aws-sdk/middleware-ssec": "3.667.0", - "@aws-sdk/middleware-user-agent": "3.669.0", - "@aws-sdk/region-config-resolver": "3.667.0", - "@aws-sdk/signature-v4-multi-region": "3.674.0", - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-endpoints": "3.667.0", - "@aws-sdk/util-user-agent-browser": "3.670.0", - "@aws-sdk/util-user-agent-node": "3.669.0", - "@aws-sdk/xml-builder": "3.662.0", - "@smithy/config-resolver": "^3.0.9", - "@smithy/core": "^2.4.8", - "@smithy/eventstream-serde-browser": "^3.0.10", - "@smithy/eventstream-serde-config-resolver": "^3.0.7", - "@smithy/eventstream-serde-node": "^3.0.9", - "@smithy/fetch-http-handler": "^3.2.9", - "@smithy/hash-blob-browser": "^3.1.6", - "@smithy/hash-node": "^3.0.7", - "@smithy/hash-stream-node": "^3.1.6", - "@smithy/invalid-dependency": "^3.0.7", - "@smithy/md5-js": "^3.0.7", - "@smithy/middleware-content-length": "^3.0.9", - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/middleware-retry": "^3.0.23", - "@smithy/middleware-serde": "^3.0.7", - "@smithy/middleware-stack": "^3.0.7", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/node-http-handler": "^3.2.4", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/url-parser": "^3.0.7", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.23", - "@smithy/util-defaults-mode-node": "^3.0.23", - "@smithy/util-endpoints": "^2.1.3", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-retry": "^3.0.7", - "@smithy/util-stream": "^3.1.9", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.670.0.tgz", - "integrity": "sha512-J+oz6uSsDvk4pimMDnKJb1wsV216zTrejvMTIL4RhUD1QPIVVOpteTdUShcjZUIZnkcJZGI+cym/SFK0kuzTpg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.667.0", - "@aws-sdk/middleware-host-header": "3.667.0", - "@aws-sdk/middleware-logger": "3.667.0", - "@aws-sdk/middleware-recursion-detection": "3.667.0", - "@aws-sdk/middleware-user-agent": "3.669.0", - "@aws-sdk/region-config-resolver": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-endpoints": "3.667.0", - "@aws-sdk/util-user-agent-browser": "3.670.0", - "@aws-sdk/util-user-agent-node": "3.669.0", - "@smithy/config-resolver": "^3.0.9", - "@smithy/core": "^2.4.8", - "@smithy/fetch-http-handler": "^3.2.9", - "@smithy/hash-node": "^3.0.7", - "@smithy/invalid-dependency": "^3.0.7", - "@smithy/middleware-content-length": "^3.0.9", - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/middleware-retry": "^3.0.23", - "@smithy/middleware-serde": "^3.0.7", - "@smithy/middleware-stack": "^3.0.7", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/node-http-handler": "^3.2.4", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/url-parser": "^3.0.7", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.23", - "@smithy/util-defaults-mode-node": "^3.0.23", - "@smithy/util-endpoints": "^2.1.3", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-retry": "^3.0.7", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.670.0.tgz", - "integrity": "sha512-4qDK2L36Q4J1lfemaHHd9ZxqKRaos3STp44qPAHf/8QyX6Uk5sXgZNVO2yWM7SIEtVKwwBh/fZAsdBkGPBfZcw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.667.0", - "@aws-sdk/credential-provider-node": "3.670.0", - "@aws-sdk/middleware-host-header": "3.667.0", - "@aws-sdk/middleware-logger": "3.667.0", - "@aws-sdk/middleware-recursion-detection": "3.667.0", - "@aws-sdk/middleware-user-agent": "3.669.0", - "@aws-sdk/region-config-resolver": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-endpoints": "3.667.0", - "@aws-sdk/util-user-agent-browser": "3.670.0", - "@aws-sdk/util-user-agent-node": "3.669.0", - "@smithy/config-resolver": "^3.0.9", - "@smithy/core": "^2.4.8", - "@smithy/fetch-http-handler": "^3.2.9", - "@smithy/hash-node": "^3.0.7", - "@smithy/invalid-dependency": "^3.0.7", - "@smithy/middleware-content-length": "^3.0.9", - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/middleware-retry": "^3.0.23", - "@smithy/middleware-serde": "^3.0.7", - "@smithy/middleware-stack": "^3.0.7", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/node-http-handler": "^3.2.4", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/url-parser": "^3.0.7", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.23", - "@smithy/util-defaults-mode-node": "^3.0.23", - "@smithy/util-endpoints": "^2.1.3", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-retry": "^3.0.7", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.670.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.670.0.tgz", - "integrity": "sha512-bExrNo8ZVWorS3cjMZKQnA2HWqDmAzcZoSN/cPVoPFNkHwdl1lzPxvcLzmhpIr48JHgKfybBjrbluDZfIYeEog==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.670.0", - "@aws-sdk/core": "3.667.0", - "@aws-sdk/credential-provider-node": "3.670.0", - "@aws-sdk/middleware-host-header": "3.667.0", - "@aws-sdk/middleware-logger": "3.667.0", - "@aws-sdk/middleware-recursion-detection": "3.667.0", - "@aws-sdk/middleware-user-agent": "3.669.0", - "@aws-sdk/region-config-resolver": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-endpoints": "3.667.0", - "@aws-sdk/util-user-agent-browser": "3.670.0", - "@aws-sdk/util-user-agent-node": "3.669.0", - "@smithy/config-resolver": "^3.0.9", - "@smithy/core": "^2.4.8", - "@smithy/fetch-http-handler": "^3.2.9", - "@smithy/hash-node": "^3.0.7", - "@smithy/invalid-dependency": "^3.0.7", - "@smithy/middleware-content-length": "^3.0.9", - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/middleware-retry": "^3.0.23", - "@smithy/middleware-serde": "^3.0.7", - "@smithy/middleware-stack": "^3.0.7", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/node-http-handler": "^3.2.4", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/url-parser": "^3.0.7", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.23", - "@smithy/util-defaults-mode-node": "^3.0.23", - "@smithy/util-endpoints": "^2.1.3", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-retry": "^3.0.7", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.667.0.tgz", - "integrity": "sha512-pMcDVI7Tmdsc8R3sDv0Omj/4iRParGY+uJtAfF669WnZfDfaBQaix2Mq7+Mu08vdjqO9K3gicFvjk9S1VLmOKA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/core": "^2.4.8", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/property-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.4", - "@smithy/signature-v4": "^4.2.0", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/util-middleware": "^3.0.7", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.667.0.tgz", - "integrity": "sha512-zZbrkkaPc54WXm+QAnpuv0LPNfsts0HPPd+oCECGs7IQRaFsGj187cwvPg9RMWDFZqpm64MdBDoA8OQHsqzYCw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.667.0.tgz", - "integrity": "sha512-sjtybFfERZWiqTY7fswBxKQLvUkiCucOWyqh3IaPo/4nE1PXRnaZCVG0+kRBPrYIxWqiVwytvZzMJy8sVZcG0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/fetch-http-handler": "^3.2.9", - "@smithy/node-http-handler": "^3.2.4", - "@smithy/property-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/util-stream": "^3.1.9", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.670.0.tgz", - "integrity": "sha512-TB1gacUj75leaTt2JsCTzygDSIk4ksv9uZoR7VenlgFPRktyOeT+fapwIVBeB2Qg7b9uxAY2K5XkKstDZyBEEw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/credential-provider-env": "3.667.0", - "@aws-sdk/credential-provider-http": "3.667.0", - "@aws-sdk/credential-provider-process": "3.667.0", - "@aws-sdk/credential-provider-sso": "3.670.0", - "@aws-sdk/credential-provider-web-identity": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/credential-provider-imds": "^3.2.4", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.670.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.670.0.tgz", - "integrity": "sha512-zwNrRYzubk4CaZ7zebeDhxsm8QtNWkbGKopZPOaZSnd5uqUGRcmx4ccVRngWUK68XDP44aEUWC8iU5Pc7btpHQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.667.0", - "@aws-sdk/credential-provider-http": "3.667.0", - "@aws-sdk/credential-provider-ini": "3.670.0", - "@aws-sdk/credential-provider-process": "3.667.0", - "@aws-sdk/credential-provider-sso": "3.670.0", - "@aws-sdk/credential-provider-web-identity": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/credential-provider-imds": "^3.2.4", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.667.0.tgz", - "integrity": "sha512-HZHnvop32fKgsNHkdhVaul7UzQ25sEc0j9yqA4bjhtbk0ECl42kj3f1pJ+ZU/YD9ut8lMJs/vVqiOdNThVdeBw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.670.0.tgz", - "integrity": "sha512-5PkA8BOy4q57Vhe9AESoHKZ7vjRbElNPKjXA4qC01xY+DitClRFz4O3B9sMzFp0PHlz9nDVSXXKgq0yzF/nAag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.670.0", - "@aws-sdk/core": "3.667.0", - "@aws-sdk/token-providers": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.667.0.tgz", - "integrity": "sha512-t8CFlZMD/1p/8Cli3rvRiTJpjr/8BO64gw166AHgFZYSN2h95L2l1tcW0jpsc3PprA32nLg1iQVKYt4WGM4ugw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.667.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.667.0.tgz", - "integrity": "sha512-XGz4jMAkDoTyFdtLz7ZF+C05IAhCTC1PllpvTBaj821z/L0ilhbqVhrT/f2Buw8Id/K5A390csGXgusXyrFFjA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "@smithy/util-config-provider": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.667.0.tgz", - "integrity": "sha512-0TiSL9S5DSG95NHGIz6qTMuV7GDKVn8tvvGSrSSZu/wXO3JaYSH0AElVpYfc4PtPRqVpEyNA7nnc7W56mMCLWQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.669.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.669.0.tgz", - "integrity": "sha512-01UQLoUzVwWMf+b+AEuwJ2lluBD+Cp8AcbyEHqvEaPdjGKHIS4BCvnY70mZYnAfRtL8R2h9tt7iI61oWU3Gjkg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.667.0.tgz", - "integrity": "sha512-Z7fIAMQnPegs7JjAQvlOeWXwpMRfegh5eCoIP6VLJIeR6DLfYKbP35JBtt98R6DXslrN2RsbTogjbxPEDQfw1w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.667.0.tgz", - "integrity": "sha512-ob85H3HhT3/u5O+x0o557xGZ78vSNeSSwMaSitxdsfs2hOuoUl1uk+OeLpi1hkuJnL41FPpokV7TVII2XrFfmg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.667.0.tgz", - "integrity": "sha512-PtTRNpNm/5c746jRgZCNg4X9xEJIwggkGJrF0GP9AB1ANg4pc/sF2Fvn1NtqPe9wtQ2stunJprnm5WkCHN7QiA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.667.0.tgz", - "integrity": "sha512-U5glWD3ehFohzpUpopLtmqAlDurGWo2wRGPNgi4SwhWU7UDt6LS7E/UvJjqC0CUrjlzOw+my2A+Ncf+fisMhxQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.674.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.674.0.tgz", - "integrity": "sha512-IvXnWrKy4mO+I44kLYHd6Wlw+FdB4sg1jvHCmnZo1KNaAFIA3x1iXgOaZynKoBdEmol3xfr2uDbeXUQvIwoIgg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/core": "^2.4.8", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/protocol-http": "^4.1.4", - "@smithy/signature-v4": "^4.2.0", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-stream": "^3.1.9", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.667.0.tgz", - "integrity": "sha512-1wuAUZIkmZIvOmGg5qNQU821CGFHhkuKioxXgNh0DpUxZ9+AeiV7yorJr+bqkb2KBFv1i1TnzGRecvKf/KvZIQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.669.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.669.0.tgz", - "integrity": "sha512-K8ScPi45zjJrj5Y2gRqVsvKKQCQbvQBfYGcBw9ZOx9TTavH80bOCBjWg/GFnvs4f37tqVc1wMN2oGvcTF6HveQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.667.0", - "@aws-sdk/types": "3.667.0", - "@aws-sdk/util-endpoints": "3.667.0", - "@smithy/core": "^2.4.8", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.667.0.tgz", - "integrity": "sha512-iNr+JhhA902JMKHG9IwT9YdaEx6KGl6vjAL5BRNeOjfj4cZYMog6Lz/IlfOAltMtT0w88DAHDEFrBd2uO0l2eg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/types": "^3.5.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.674.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.674.0.tgz", - "integrity": "sha512-VMQWbtcbg4FV/fILrODADV21pPg9AghuEzQlW2kH0hCtacvBwFl7eBxIiCBLLtkNple+CVPJvyBcqOZdBkEv/w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.674.0", - "@aws-sdk/types": "3.667.0", - "@smithy/protocol-http": "^4.1.4", - "@smithy/signature-v4": "^4.2.0", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.667.0.tgz", - "integrity": "sha512-ZecJlG8p6D4UTYlBHwOWX6nknVtw/OBJ3yPXTSajBjhUlj9lE2xvejI8gl4rqkyLXk7z3bki+KR4tATbMaM9yg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.667.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.667.0.tgz", - "integrity": "sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", - "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.667.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.667.0.tgz", - "integrity": "sha512-X22SYDAuQJWnkF1/q17pkX3nGw5XMD9YEUbmt87vUnRq7iyJ3JOpl6UKOBeUBaL838wA5yzdbinmCITJ/VZ1QA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/types": "^3.5.0", - "@smithy/util-endpoints": "^2.1.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", - "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.670.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.670.0.tgz", - "integrity": "sha512-iRynWWazqEcCKwGMcQcywKTDLdLvqts1Yx474U64I9OKQXXwhOwhXbF5CAPSRta86lkVNAVYJa/0Bsv45pNn1A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.667.0", - "@smithy/types": "^3.5.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.669.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.669.0.tgz", - "integrity": "sha512-9jxCYrgggy2xd44ZASqI7AMiRVaSiFp+06Kg8BQSU0ijKpBJlwcsqIS8pDT/n6LxuOw2eV5ipvM2C0r1iKzrGA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.669.0", - "@aws-sdk/types": "3.667.0", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.662.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.662.0.tgz", - "integrity": "sha512-ikLkXn0igUpnJu2mCZjklvmcDGWT9OaLRv3JyC/cRkTaaSrblPjPM7KKsltxdMTLQ+v7fjCN0TsJpxphMfaOPA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.25.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/eslint-parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.8.tgz", - "integrity": "sha512-Po3VLMN7fJtv0nsOjBDSbO1J71UhzShE9MuOSkWEV9IZQXzhZklYtzKZ8ZD/Ij3a0JBv1AG3Ny2L3jvAHQVOGg==", - "license": "MIT", - "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/@babel/eslint-parser/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", - "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.9.tgz", - "integrity": "sha512-hJhBCb0+NnTWybvWq2WpbCYDOcflSbx0t+BYP65e5R9GVnukiDTi+on5bFkk4p7QGuv190H6KfNiV9Knf/3cZA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.23.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-decorators": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.23.3.tgz", - "integrity": "sha512-cf7Niq4/+/juY67E0PbgH0TDhLQ5J7zS8C/Q5FFx+DWyrRa9sUQdTXkjqKu8zGvuqr7vw1muKiukseihU+PJDA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz", - "integrity": "sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz", - "integrity": "sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", - "license": "MIT", - "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.23.3.tgz", - "integrity": "sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", - "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", - "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", - "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", - "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", - "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.23.3", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", - "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "license": "MIT" - }, - "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "license": "MIT" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@csstools/normalize.css": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", - "integrity": "sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==", - "license": "CC0-1.0" - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "license": "CC0-1.0", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", - "license": "CC0-1.0", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.0.10" - } - }, - "node_modules/@digitalbazaar/http-client": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-1.2.0.tgz", - "integrity": "sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q==", - "license": "BSD-3-Clause", - "dependencies": { - "esm": "^3.2.22", - "ky": "^0.25.1", - "ky-universal": "^0.8.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", - "license": "MIT" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, - "node_modules/@emotion/react": { - "version": "11.11.3", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", - "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", - "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", - "license": "MIT", - "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", - "license": "MIT" - }, - "node_modules/@emotion/styled": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" - }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", - "license": "MIT" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", - "license": "MIT" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", - "license": "MIT" - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@fingerprintjs/fingerprintjs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.2.2.tgz", - "integrity": "sha512-scD+pDgNZW78LuFAr7ms2yxmDx2NWC4+K5iiOjPT2ZlTlHFbLsORUzLJI2rcKicxxLtHbvf3A7BU1drVr4iHGg==", - "license": "BUSL-1.1", - "dependencies": { - "tslib": "^2.4.1" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.1.tgz", - "integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.6.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==", - "license": "MIT" - }, - "node_modules/@gilbarbara/deep-equal": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", - "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==", - "license": "MIT" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "license": "MIT", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "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-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/environment/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/environment/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/environment/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/environment/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/environment/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.3.tgz", - "integrity": "sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ==", - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/fake-timers/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/fake-timers/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/globals/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/globals/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/globals/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/globals/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/globals/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.25.16" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "license": "MIT", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/test-result/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/test-result/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/test-result/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/test-result/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-result/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "license": "MIT", - "dependencies": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.3.tgz", - "integrity": "sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.34", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.34.tgz", - "integrity": "sha512-e2mbTGTtReD/y5RFwnhkl1Tgl3XwgJhY040IlfkTVaU9f5LWrVhEnpRsYXu3B1CtLrwiWs4cu7aMHV9yRd4jpw==", - "deprecated": "This package has been replaced by @base-ui-components/react", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.7", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.7.tgz", - "integrity": "sha512-AuF+Wo2Mp/edaO6vJnWjg+gj4tzEz5ChMZnAQpc22DXpSvM8ddgGcZvM7D7F99pIBoSv8ub+Iz0viL+yuGVmhg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, - "node_modules/@mui/icons-material": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.7.tgz", - "integrity": "sha512-EDAc8TVJGIA/imAvR3u4nANl2W5h3QeHieu2gK7Ypez/nIA55p08tHjf8UrMXEpxCAvfZO6piY9S9uaxETdicA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.7.tgz", - "integrity": "sha512-l6+AiKZH3iOJmZCnlpel8ghYQe9Lq0BEuKP8fGj3g5xz4arO9GydqYAtLPMvuHKtArj8lJGNuT2yHYxmejincA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.34", - "@mui/core-downloads-tracker": "^5.15.7", - "@mui/system": "^5.15.7", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.7", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "csstype": "^3.1.2", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", - "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.16.6", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.7.tgz", - "integrity": "sha512-ixSdslOjK1kzdGcxqj7O3d14By/LPQ7EWknsViQ8RaeT863EAQemS+zvUJDTcOpkfJh6q6gPnYMIb2TJCs9eWA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/styles": { - "version": "5.16.7", - "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.16.7.tgz", - "integrity": "sha512-FfXhHP/2MlqH+vLs2tIHMeCChmqSRgkOALVNLKkPrDsvtoq5J8OraOutCn1scpvRjr9mO8ZhW6jKx2t/vUDxtQ==", - "deprecated": "Deprecated, check the migration instruction in https://mui.com/material-ui/migration/migrating-from-jss/", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/hash": "^0.9.1", - "@mui/private-theming": "^5.16.6", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.6", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "hoist-non-react-statics": "^3.3.2", - "jss": "^10.10.0", - "jss-plugin-camel-case": "^10.10.0", - "jss-plugin-default-unit": "^10.10.0", - "jss-plugin-global": "^10.10.0", - "jss-plugin-nested": "^10.10.0", - "jss-plugin-props-sort": "^10.10.0", - "jss-plugin-rule-value-function": "^10.10.0", - "jss-plugin-vendor-prefixer": "^10.10.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.7.tgz", - "integrity": "sha512-9alZ4/dLxsTwUOdqakgzxiL5YW6ntqj0CfzWImgWnBMTZhgGcPsbYpBLniNkkk7/jptma4/bykWXHwju/ls/pg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.7", - "@mui/styled-engine": "^5.15.7", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.7", - "clsx": "^2.1.0", - "csstype": "^3.1.2", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.18", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.18.tgz", - "integrity": "sha512-uvK9dWeyCJl/3ocVnTOS6nlji/Knj8/tVqVX03UVTpdmTJYu/s4jtDd9Kvv0nRGE0CUSNW1UYAci7PYypjealg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", - "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/types": "^7.2.15", - "@types/prop-types": "^15.7.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.3.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "license": "MIT", - "dependencies": { - "eslint-scope": "5.1.1" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@number-flow/react": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@number-flow/react/-/react-0.5.10.tgz", - "integrity": "sha512-a8Wh5eNITn7Km4xbddAH7QH8eNmnduR6k34ER1hkHSGO4H2yU1DDnuAWLQM99vciGInFODemSc0tdxrXkJEpbA==", - "license": "MIT", - "dependencies": { - "esm-env": "^1.1.4", - "number-flow": "0.5.8" - }, - "peerDependencies": { - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", - "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", - "license": "MIT", - "dependencies": { - "ansi-html": "^0.0.9", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^4.2.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <5.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x || 5.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@project-sunbird/client-services": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@project-sunbird/client-services/-/client-services-5.1.2.tgz", - "integrity": "sha512-E2xvnYrenDIyyIMFYLy3xnsWQ0yG46eGEUFXUwmaL5BJK7qsjOU4zHHMqAhQYjEEJTZ9iDhLgcRsjf8oL8RhRQ==", - "license": "MIT", - "dependencies": { - "@project-sunbird/telemetry-sdk": "0.0.26", - "dayjs": "^1.8.20", - "inversify": "^5.0.1", - "jsonld": "^5.2.0", - "jsonld-signatures": "^6.0.0", - "jszip": "^3.7.1", - "node-fetch": "2.6.5", - "qs": "^6.9.7", - "reflect-metadata": "^0.1.13", - "whatwg-fetch": "^3.1.0" - }, - "peerDependencies": { - "rxjs": "6.x.x" - } - }, - "node_modules/@project-sunbird/telemetry-sdk": { - "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@project-sunbird/telemetry-sdk/-/telemetry-sdk-0.0.26.tgz", - "integrity": "sha512-8r8tf+YN+z8A4KHBmL8/wxDIKGkASDHMMxyhlI9/iWv83sueoN5qHB6QnqgtIRyEdFjt4Tum5u18/oB71Bb0PQ==", - "license": "MIT", - "dependencies": { - "grunt-karma": "^0.12.2", - "karma": "^3.0.0" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@redux-saga/core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.3.0.tgz", - "integrity": "sha512-L+i+qIGuyWn7CIg7k1MteHGfttKPmxwZR5E7OsGikCL2LzYA0RERlaUY00Y3P3ZV2EYgrsYlBrGs6cJP5OKKqA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.6.3", - "@redux-saga/deferred": "^1.2.1", - "@redux-saga/delay-p": "^1.2.1", - "@redux-saga/is": "^1.1.3", - "@redux-saga/symbols": "^1.1.3", - "@redux-saga/types": "^1.2.1", - "typescript-tuple": "^2.2.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/redux-saga" - } - }, - "node_modules/@redux-saga/deferred": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", - "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==", - "license": "MIT" - }, - "node_modules/@redux-saga/delay-p": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", - "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", - "license": "MIT", - "dependencies": { - "@redux-saga/symbols": "^1.1.3" - } - }, - "node_modules/@redux-saga/is": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", - "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", - "license": "MIT", - "dependencies": { - "@redux-saga/symbols": "^1.1.3", - "@redux-saga/types": "^1.2.1" - } - }, - "node_modules/@redux-saga/symbols": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", - "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==", - "license": "MIT" - }, - "node_modules/@redux-saga/types": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", - "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==", - "license": "MIT" - }, - "node_modules/@reduxjs/toolkit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.3.0.tgz", - "integrity": "sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA==", - "license": "MIT", - "dependencies": { - "immer": "^10.0.3", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@reduxjs/toolkit/node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" - }, - "node_modules/@remix-run/router": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", - "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "license": "MIT", - "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/pluginutils/node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", - "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz", - "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz", - "integrity": "sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz", - "integrity": "sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz", - "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.8", - "@smithy/types": "^3.5.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.8.tgz", - "integrity": "sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/middleware-retry": "^3.0.23", - "@smithy/middleware-serde": "^3.0.7", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz", - "integrity": "sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.8", - "@smithy/property-provider": "^3.1.7", - "@smithy/types": "^3.5.0", - "@smithy/url-parser": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.6.tgz", - "integrity": "sha512-SBiOYPBH+5wOyPS7lfI150ePfGLhnp/eTu5RnV9xvhGvRiKfnl6HzRK9wehBph+il8FxS9KTeadx7Rcmf1GLPQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.5.0", - "@smithy/util-hex-encoding": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.10.tgz", - "integrity": "sha512-1i9aMY6Pl/SmA6NjvidxnfBLHMPzhKu2BP148pEt5VwhMdmXn36PE2kWKGa9Hj8b0XGtCTRucpCncylevCtI7g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.9", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.7.tgz", - "integrity": "sha512-eVzhGQBPEqXXYHvIUku0jMTxd4gDvenRzUQPTmKVWdRvp9JUCKrbAXGQRYiGxUYq9+cqQckRm0wq3kTWnNtDhw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.9.tgz", - "integrity": "sha512-JE0Guqvt0xsmfQ5y1EI342/qtJqznBv8cJqkHZV10PwC8GWGU5KNgFbQnsVCcX+xF+qIqwwfRmeWoJCjuOLmng==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.9", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.9.tgz", - "integrity": "sha512-bydfgSisfepCufw9kCEnWRxqxJFzX/o8ysXWv+W9F2FIyiaEwZ/D8bBKINbh4ONz3i05QJ1xE7A5OKYvgJsXaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^3.1.6", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", - "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.4", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.6.tgz", - "integrity": "sha512-BKNcMIaeZ9lB67sgo88iCF4YB35KT8X2dNJ8DqrtZNTgN6tUDYBKThzfGtos/mnZkGkW91AYHisESHmSiYQmKw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^3.0.0", - "@smithy/chunked-blob-reader-native": "^3.0.0", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/hash-node": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.7.tgz", - "integrity": "sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.6.tgz", - "integrity": "sha512-sFSSt7cmCpFWZPfVx7k80Bgb1K2VJ27VmMxH8X+dDhp7Wv8IBgID4K2VK5ehMJROF8hQgcj4WywnkHIwX/xlwQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz", - "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.7.tgz", - "integrity": "sha512-+wco9IN9uOW4tNGkZIqTR6IXyfO7Z8A+IOq82QCRn/f/xcmt7H1fXwmQVbfDSvbeFwfNnhv7s+u0G9PzPG6o2w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz", - "integrity": "sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz", - "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.7", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "@smithy/url-parser": "^3.0.7", - "@smithy/util-middleware": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.23.tgz", - "integrity": "sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.8", - "@smithy/protocol-http": "^4.1.4", - "@smithy/service-error-classification": "^3.0.7", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-retry": "^3.0.7", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz", - "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz", - "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", - "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz", - "integrity": "sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.5", - "@smithy/protocol-http": "^4.1.4", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz", - "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.4.tgz", - "integrity": "sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz", - "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz", - "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz", - "integrity": "sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", - "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.0.tgz", - "integrity": "sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.7", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.0.tgz", - "integrity": "sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/middleware-stack": "^3.0.7", - "@smithy/protocol-http": "^4.1.4", - "@smithy/types": "^3.5.0", - "@smithy/util-stream": "^3.1.9", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.5.0.tgz", - "integrity": "sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.7.tgz", - "integrity": "sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^3.0.7", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.23.tgz", - "integrity": "sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.7", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.23.tgz", - "integrity": "sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^3.0.9", - "@smithy/credential-provider-imds": "^3.2.4", - "@smithy/node-config-provider": "^3.1.8", - "@smithy/property-provider": "^3.1.7", - "@smithy/smithy-client": "^3.4.0", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz", - "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.8", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.7.tgz", - "integrity": "sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz", - "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.7", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz", - "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^3.2.9", - "@smithy/node-http-handler": "^3.2.4", - "@smithy/types": "^3.5.0", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.6.tgz", - "integrity": "sha512-xs/KAwWOeCklq8aMlnpk25LgxEYHKOEodfjfKclDMLcBJEVEKzDLxZxBQyztcuPJ7F54213NJS8PxoiHNMdItQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.5", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", - "license": "Apache-2.0", - "dependencies": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "license": "MIT", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "license": "MIT", - "dependencies": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.6" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@tekdi/all-telemetry-sdk": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@tekdi/all-telemetry-sdk/-/all-telemetry-sdk-0.0.32.tgz", - "integrity": "sha512-TFRKsMD2StLtUTyoVkVgsgMW9nIsRotxZ7qs/VxGSd55hc/LHc82kuF6pMlT+h6Y6wJHm67Kfk227Vb2Ykttog==", - "license": "MIT", - "dependencies": { - "grunt-karma": "^0.12.2", - "karma": "^3.0.0" - } - }, - "node_modules/@tekdi/multilingual-profanity-filter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@tekdi/multilingual-profanity-filter/-/multilingual-profanity-filter-1.1.0.tgz", - "integrity": "sha512-DOZGHFi2h11/0iMnAkmnCArD/1CV2Hp5jovmUuzqYzneWiLQL9CdHHf1cM1bWa1BbZ/8QEjx15kJ4ju00nv2RQ==", - "license": "MIT" - }, - "node_modules/@testing-library/dom": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", - "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@testing-library/dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@testing-library/dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@testing-library/dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", - "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", - "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@testing-library/jest-dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "license": "MIT" - }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.12", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", - "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", - "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "license": "MIT" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.15", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", - "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", - "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "license": "MIT" - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "18.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz", - "integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==", - "license": "MIT" - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT" - }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "license": "MIT" - }, - "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "license": "MIT" - }, - "node_modules/@types/q": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", - "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", - "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.2.50", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.50.tgz", - "integrity": "sha512-y0XIDJkqp9HynS1VBktZG9mUziHTK5WZTAFDP/UfzSq+poV1drUKsr4VkjMyHTbqMz26BwgLZVYdx/EgPm7EkQ==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT" - }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "license": "MIT" - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "license": "MIT" - }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", - "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", - "license": "MIT", - "dependencies": { - "@types/jest": "*" - } - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT" - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", - "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", - "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "license": "ISC" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xenova/transformers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@xenova/transformers/-/transformers-2.1.0.tgz", - "integrity": "sha512-TDI4Kb73+XrQYtrlNvFms1XOPFcjx2SMygEVtl706EsRLyFUx6LEVJzhweGBTl+/wV3zpVOgYa6VJw1ElbzVGQ==", - "license": "Apache-2.0", - "dependencies": { - "onnxruntime-web": "1.14.0", - "sharp": "^0.32.0" - }, - "optionalDependencies": { - "onnxruntime-node": "1.14.0" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "license": "BSD-3-Clause" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "license": "MIT", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==", - "license": "MIT" - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", - "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "license": "ISC", - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/anymatch/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/anymatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/anymatch/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/anymatch/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/anymatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/anymatch/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "license": "MIT", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "license": "MIT", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.6.tgz", - "integrity": "sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", - "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "license": "MIT" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "license": "MIT" - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "license": "MIT" - }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "license": "MIT" - }, - "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axios": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", - "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0" - }, - "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "license": "MIT", - "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-jest/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", - "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", - "license": "MIT", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.4", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/babel-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/babel-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "license": "MIT", - "peerDependencies": { - "@babel/core": "^7.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", - "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.5.0", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", - "license": "MIT" - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/bare-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.5.tgz", - "integrity": "sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", - "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", - "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "license": "MIT", - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha512-437oANT9tP582zZMwSvZGy2nmSeAb8DW2me3y+Uv1Wp2Rulr8Mqlyrv3E7MLxmsiaPSMMDmiDVzgE+e8zlMx9g==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha512-rz8L+d/xByiB/vLVftPkyY215fqNrmasrcJsYkVcm4TgJNz+YXKrFaFAWibSaHkiKoSgMDCb+lipOIRQNGYesw==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/base64url-universal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/base64url-universal/-/base64url-universal-1.1.0.tgz", - "integrity": "sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA==", - "license": "BSD-3-Clause", - "dependencies": { - "base64url": "^3.0.0" - }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "license": "MIT" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha512-bYeph2DFlpK1XmGs6fvlLRUN29QISM3GBuUwSFsMY2XRx4AvC0WNCS57j4c/xGrK2RS24C1w3YoBOsw9fT46tQ==", - "dependencies": { - "callsite": "1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/bfj": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", - "integrity": "sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==", - "license": "MIT", - "dependencies": { - "bluebird": "^3.7.2", - "check-types": "^11.2.3", - "hoopy": "^0.1.4", - "jsonpath": "^1.1.1", - "tryer": "^1.0.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "license": "MIT" - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/bonjour-service": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", - "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "license": "BSD-2-Clause" - }, - "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "license": "MIT", - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "license": "MIT", - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "license": "MIT" - }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "license": "MIT" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "license": "MIT", - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", - "engines": { - "node": "*" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/canonicalize": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", - "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", - "license": "Apache-2.0" - }, - "node_modules/canvas-confetti": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz", - "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==", - "license": "ISC", - "funding": { - "type": "donate", - "url": "https://www.paypal.me/kirilvatev" - } - }, - "node_modules/case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "license": "Apache-2.0" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-error-rate": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-error-rate/-/character-error-rate-1.1.4.tgz", - "integrity": "sha512-VDVylpiUdLOqY9aowYsz9M3zKdeQAdFF76PI1lv3oBQANQ6bc6V7pGEqar9nx6c37x0UMu5EHg32Sus3fQ1XxQ==", - "license": "MIT" - }, - "node_modules/check-types": { - "version": "11.2.3", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", - "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "license": "MIT", - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "license": "ISC", - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/circular-json": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", - "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", - "license": "MIT" - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", - "license": "MIT" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "license": "MIT", - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "license": "MIT", - "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "license": "MIT" - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", - "license": "MIT", - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha512-4Mi0V7N48B9KzC8Zl/U7wiWuxMFEHf44N3/PSoAvWDu8IOPrddNo1y1tC/kXbP7IvVMhgCFMMNzgKb0pWoin9w==", - "license": "MIT", - "dependencies": { - "lodash": "^4.5.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "license": "MIT" - }, - "node_modules/component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==" - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz", - "integrity": "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.22.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.38.1.tgz", - "integrity": "sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/crypto-ld": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/crypto-ld/-/crypto-ld-3.9.0.tgz", - "integrity": "sha512-PFE7V6A2QNnUp6iiPVEZI4p8wsztkEWLbY1BAXVnclm/aw4KGwpJ+1Ds4vQUCJ5BsWxj15fwE5rHQ8AWaWB2nw==", - "license": "BSD-3-Clause", - "dependencies": { - "base64url-universal": "^1.0.1", - "bs58": "^4.0.1", - "node-forge": "~0.10.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8.3.0" - }, - "optionalDependencies": { - "sodium-native": "^3.2.0" - } - }, - "node_modules/crypto-ld/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "license": "MIT", - "dependencies": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "license": "CC0-1.0", - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "license": "MIT" - }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-vendor": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", - "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.3", - "is-in-browser": "^1.0.2" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "license": "MIT" - }, - "node_modules/cssdb": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", - "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ], - "license": "CC0-1.0" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", - "license": "MIT", - "dependencies": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "license": "MIT", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "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-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-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-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "license": "MIT", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "license": "MIT", - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "license": "CC0-1.0" - }, - "node_modules/csso/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "license": "MIT", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "license": "MIT" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "license": "BSD-2-Clause" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "license": "MIT", - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/data-urls/node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "license": "MIT", - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/date-format": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", - "integrity": "sha512-lAJqBmFzCLcDJdI9cEnJ7loSkLTh1PbIgZUndlzvYbf6NyFEr5n9rQhOwr6CIGwZqyQ3sYeQQiP9NOVQmgmRMA==", - "deprecated": "1.x is no longer supported. Please upgrade to 4.x or higher.", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "license": "MIT" - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "license": "MIT" - }, - "node_modules/deep-diff": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", - "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==", - "license": "MIT" - }, - "node_modules/deep-equal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", - "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.2", - "get-intrinsic": "^1.1.3", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "license": "BSD-2-Clause", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" - }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "license": "MIT" - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", - "license": "MIT", - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "deprecated": "Use your platform's native DOMException instead", - "license": "MIT", - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=10" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "license": "BSD-2-Clause" - }, - "node_modules/double-metaphone": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/double-metaphone/-/double-metaphone-2.0.1.tgz", - "integrity": "sha512-oHqGfNo2bG/VTZSGFz98TWpyv3WD+d9TDOxwAY8L+zqwm4sGawNvEynegjPU+4HowE4HnIXSYAq0lst2YJgfzA==", - "license": "MIT", - "bin": { - "double-metaphone": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.41", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", - "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" - } - }, - "node_modules/engine.io-client": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", - "license": "MIT", - "dependencies": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - } - }, - "node_modules/engine.io-client/node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==", - "license": "MIT" - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, - "node_modules/engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", - "license": "MIT", - "dependencies": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/engine.io/node_modules/ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", - "license": "MIT" - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", - "license": "MIT", - "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "license": "MIT" - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "license": "BSD-3-Clause", - "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@babel/plugin-syntax-flow": "^7.14.5", - "@babel/plugin-transform-react-jsx": "^7.14.9", - "eslint": "^8.1.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=5.0.0", - "prettier": ">=1.13.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-testing-library": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", - "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^5.58.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0", - "npm": ">=6" - }, - "peerDependencies": { - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", - "license": "MIT", - "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/esm-env": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "license": "MIT" - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha512-zOOsEnAhvIxxd0esCNbYG2xerGf46niZ1egS43eV7Fu4t7VIScgPXMcMabCLaPrqkzwvwo6zZipDiX3t0ILF2w==", - "license": "MIT", - "dependencies": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-braces/node_modules/array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-braces/node_modules/braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha512-EIMHIv2UXHWFY2xubUGKz+hq9hNkENj4Pjvr7h58cmJgpkK2yMlKA8I484f7MSttkzVAy/lL7X9xDaILd6avzA==", - "dependencies": { - "expand-range": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "license": "MIT", - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha512-busOHJ0t7t5UcutcyNDqmaDX+1cb0XlqsAUgTlmplVv0rIqBaMcBSZRLlkDm0nxtl8O3o/EvRRrdQ/WnyPERLQ==", - "dependencies": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha512-la5kPULwIgkSSaZj9w7/A1uHqOBAgOhDUKQ5CkfL8LZ4Si6r4+2D0hI6b4o60MW4Uj2yNJARWIZUDPxlvOYQcw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha512-yHeI3F9v20MY+8/5WAUgIWseMZwpLD+l9h5hGyzh6fQjhle2AwjjRDao1m5IozSDuVvMw09/mvE8AU1oDmZKpQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.3.tgz", - "integrity": "sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg==", - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.4.3", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-util": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "license": "MIT", - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "license": "BSD-3-Clause" - }, - "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fetch-blob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz", - "integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==", - "license": "MIT", - "engines": { - "node": "^10.17.0 || >=12.3.0" - }, - "peerDependenciesMeta": { - "domexception": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/file-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/file-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT", - "optional": true - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "license": "MIT" - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatbuffers": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz", - "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==", - "license": "SEE LICENSE IN LICENSE.txt" - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", - "license": "MIT", - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/framer-motion": { - "version": "12.4.1", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.1.tgz", - "integrity": "sha512-5Ijbea3topSZjadQ0hgc/TcWj2ldMZmNREM7RvAhvsThYOA1HHOA8TT1yKvMu1YXP3jWaFwoZ6Vo9Nw+DUZrzA==", - "license": "MIT", - "dependencies": { - "motion-dom": "^12.0.0", - "motion-utils": "^12.0.0", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "license": "ISC" - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "license": "MIT" - }, - "node_modules/grunt-karma": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/grunt-karma/-/grunt-karma-0.12.2.tgz", - "integrity": "sha512-houkUR8sks8uliJ5khwvV4Cf2vrxSPkvCt+w9uwOOHhDOCLZ9ZE4/MeDmC7GgSajGK+h/svpacbKFHyjNbCsng==", - "license": "MIT", - "dependencies": { - "lodash": "^3.10.1" - }, - "peerDependencies": { - "grunt": ">=0.4.x", - "karma": "^0.13.0 || >= 0.14.0-rc.0" - } - }, - "node_modules/grunt-karma/node_modules/lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==", - "license": "MIT" - }, - "node_modules/guid-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", - "license": "ISC" - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "license": "MIT" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/har-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", - "license": "(Apache-2.0 OR MPL-1.1)" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "license": "MIT", - "dependencies": { - "isarray": "2.0.1" - } - }, - "node_modules/has-binary2/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==", - "license": "MIT" - }, - "node_modules/has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==", - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "license": "MIT", - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/homophones": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homophones/-/homophones-1.0.1.tgz", - "integrity": "sha512-fXEpWXOy5RqP2f68HFTT3ejvV+z4qV1CGw/Blz+VsYwhSTToNNdKThScJVkkzz/y9G6+3aTxL3dptRXjwn7jmQ==", - "license": "MIT", - "dependencies": { - "axios": "^1.4.0", - "ts-node": "^10.9.1", - "typescript": "^5.1.6" - } - }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/howler": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", - "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==", - "license": "MIT" - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "license": "MIT" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.2.tgz", - "integrity": "sha512-q7xp/FO9RGBVoTKNItkdX1jKLscLFkgn/dLVFNYbHVbfHLBk6DYW5nsQ8kCzIWcgKP/kUBocetjvav6lD8YfCQ==", - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", - "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/husky": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", - "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", - "dev": true, - "license": "MIT", - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/hyphenate-style-name": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", - "license": "ISC" - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "license": "MIT", - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, - "node_modules/immer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", - "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/inversify": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.1.1.tgz", - "integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==", - "license": "MIT" - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", - "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "license": "MIT" - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", - "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-lite": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz", - "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==", - "license": "MIT" - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "license": "MIT" - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "license": "MIT" - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "license": "MIT", - "dependencies": { - "buffer-alloc": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "license": "MIT" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "license": "MIT", - "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-changed-files/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-changed-files/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-circus/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "license": "MIT", - "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.3.tgz", - "integrity": "sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-jsdom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-environment-jsdom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-node/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-environment-node/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-haste-map/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-haste-map/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-haste-map/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-jasmine2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-jasmine2/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "license": "MIT", - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz", - "integrity": "sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.3.tgz", - "integrity": "sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.4.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-mock/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-mock/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-mock/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-mock/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-mock/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-resolve-dependencies/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "license": "MIT", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.3.tgz", - "integrity": "sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.4.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-validate/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "jest": "^27.0.0 || ^28.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "license": "MIT", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "license": "MIT" - }, - "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-watch-typeahead/node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "license": "MIT", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "license": "MIT", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "license": "MIT", - "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "license": "MIT", - "dependencies": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "license": "MIT", - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "license": "MIT" - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "license": "MIT", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsdom/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "license": "MIT", - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "license": "ISC" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonld": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-5.2.0.tgz", - "integrity": "sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw==", - "license": "BSD-3-Clause", - "dependencies": { - "@digitalbazaar/http-client": "^1.1.0", - "canonicalize": "^1.0.1", - "lru-cache": "^6.0.0", - "rdf-canonize": "^3.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jsonld-signatures": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/jsonld-signatures/-/jsonld-signatures-6.0.0.tgz", - "integrity": "sha512-GIh5DCZ5g5vQBAFcjSDHmYyTaD/fYh5KwlAiKgx83VRzcQNNgzeG7DsLdXcgyCxEai9qIM9u2frZAInKDN5+og==", - "license": "BSD-3-Clause", - "dependencies": { - "base64url": "^3.0.1", - "crypto-ld": "^3.7.0", - "jsonld": "^2.0.2", - "node-forge": "^0.10.0", - "security-context": "^4.0.0", - "serialize-error": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jsonld-signatures/node_modules/jsonld": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-2.0.2.tgz", - "integrity": "sha512-/TQzRe75/3h2khu57IUojha5oat+M82bm8RYw0jLhlmmPrW/kTWAZ9nGzKPfZWnPYnVVJJMQVc/pU8HCmpv9xg==", - "license": "BSD-3-Clause", - "dependencies": { - "canonicalize": "^1.0.1", - "lru-cache": "^5.1.1", - "rdf-canonize": "^1.0.2", - "request": "^2.88.0", - "semver": "^6.3.0", - "xmldom": "0.1.19" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonld-signatures/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/jsonld-signatures/node_modules/rdf-canonize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-1.2.0.tgz", - "integrity": "sha512-MQdcRDz4+82nUrEb3hNQangBDpmep15uMmnWclGi/1KS0bNVc8oHpoNI0PFLHZsvwgwRzH31bO1JAScqUAstvw==", - "license": "BSD-3-Clause", - "dependencies": { - "node-forge": "^0.10.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonld-signatures/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/jsonld-signatures/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/jsonpath": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", - "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", - "license": "MIT", - "dependencies": { - "esprima": "1.2.2", - "static-eval": "2.0.2", - "underscore": "1.12.1" - } - }, - "node_modules/jsonpath/node_modules/esprima": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", - "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/jss": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", - "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "csstype": "^3.0.2", - "is-in-browser": "^1.1.3", - "tiny-warning": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/jss" - } - }, - "node_modules/jss-plugin-camel-case": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", - "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "hyphenate-style-name": "^1.0.3", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-default-unit": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", - "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-global": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", - "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-nested": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", - "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-props-sort": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", - "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-rule-value-function": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", - "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-vendor-prefixer": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", - "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "css-vendor": "^2.0.8", - "jss": "10.10.0" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/jwt-decode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", - "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/karma": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", - "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", - "license": "MIT", - "dependencies": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "chokidar": "^2.0.3", - "colors": "^1.1.0", - "combine-lists": "^1.0.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", - "flatted": "^2.0.0", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.5", - "log4js": "^3.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", - "socket.io": "2.1.1", - "source-map": "^0.6.1", - "tmp": "0.0.33", - "useragent": "2.3.0" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/karma/node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "license": "ISC" - }, - "node_modules/karma/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ky": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", - "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky?sponsor=1" - } - }, - "node_modules/ky-universal": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", - "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "node-fetch": "3.0.0-beta.9" - }, - "engines": { - "node": ">=10.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" - }, - "peerDependencies": { - "ky": ">=0.17.0", - "web-streams-polyfill": ">=2.0.0" - }, - "peerDependenciesMeta": { - "web-streams-polyfill": { - "optional": true - } - } - }, - "node_modules/ky-universal/node_modules/node-fetch": { - "version": "3.0.0-beta.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", - "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^3.0.1", - "fetch-blob": "^2.1.1" - }, - "engines": { - "node": "^10.17 || >=12.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/launch-editor": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", - "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/lint-staged": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-11.2.6.tgz", - "integrity": "sha512-Vti55pUnpvPE0J9936lKl0ngVeTdSZpEdTNhASbkaWX7J5R9OEifo1INBGQuGW4zmy6OG+TcWPJ3m5yuy5Q8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "2.1.0", - "colorette": "^1.4.0", - "commander": "^8.2.0", - "cosmiconfig": "^7.0.1", - "debug": "^4.3.2", - "enquirer": "^2.3.6", - "execa": "^5.1.1", - "listr2": "^3.12.2", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.2.0", - "string-argv": "0.3.1", - "stringify-object": "3.3.0", - "supports-color": "8.1.1" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/lint-staged/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lint-staged/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "license": "MIT" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "license": "MIT" - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "license": "MIT" - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-update/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log4js": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", - "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", - "deprecated": "3.x is no longer supported. Please upgrade to 6.x or higher.", - "license": "Apache-2.0", - "dependencies": { - "circular-json": "^0.5.5", - "date-format": "^1.2.0", - "debug": "^3.1.0", - "rfdc": "^1.1.2", - "streamroller": "0.7.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/log4js/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "license": "MIT", - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "license": "MIT", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "license": "MIT", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "license": "CC0-1.0" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", - "license": "MIT" - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/metaphone": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/metaphone/-/metaphone-2.0.1.tgz", - "integrity": "sha512-YR9RHhgIjoS77yhz84De0ffUKFGTmF58a+xXFNkbHSK/aBPznGKhjR2p29rYSPs0t885/AKKZCOdUn7N6rpKiA==", - "license": "MIT", - "bin": { - "metaphone": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/micromatch/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", - "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", - "license": "MIT" - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "license": "MIT", - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/mkdirp/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/motion-dom": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.0.0.tgz", - "integrity": "sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==", - "license": "MIT", - "dependencies": { - "motion-utils": "^12.0.0" - } - }, - "node_modules/motion-utils": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz", - "integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nan": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", - "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "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/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "license": "MIT", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/nanomatch/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "license": "MIT" - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", - "license": "MIT", - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/number-flow": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/number-flow/-/number-flow-0.5.8.tgz", - "integrity": "sha512-FPr1DumWyGi5Nucoug14bC6xEz70A1TnhgSHhKyfqjgji2SOTz+iLJxKtv37N5JyJbteGYCm6NQ9p1O4KZ7iiA==", - "license": "MIT", - "dependencies": { - "esm-env": "^1.1.4" - } - }, - "node_modules/nwsapi": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", - "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", - "license": "MIT" - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha512-S0sN3agnVh2SZNEIGc0N1X4Z5K0JeFbGBrnuZpsxuUh5XLF0BnvWkMjRXo/zGKLd/eghvNIKcx1pQkmUjXIyrA==" - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", - "license": "MIT", - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", - "license": "MIT", - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz", - "integrity": "sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==", - "license": "MIT", - "dependencies": { - "array.prototype.reduce": "^1.0.6", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "safe-array-concat": "^1.0.0" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onnx-proto": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/onnx-proto/-/onnx-proto-4.0.4.tgz", - "integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==", - "license": "MIT", - "dependencies": { - "protobufjs": "^6.8.8" - } - }, - "node_modules/onnxruntime-common": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz", - "integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew==", - "license": "MIT" - }, - "node_modules/onnxruntime-node": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz", - "integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==", - "license": "MIT", - "optional": true, - "os": [ - "win32", - "darwin", - "linux" - ], - "dependencies": { - "onnxruntime-common": "~1.14.0" - } - }, - "node_modules/onnxruntime-web": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz", - "integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==", - "license": "MIT", - "dependencies": { - "flatbuffers": "^1.12.0", - "guid-typescript": "^1.0.9", - "long": "^4.0.0", - "onnx-proto": "^4.0.4", - "onnxruntime-common": "~1.14.0", - "platform": "^1.3.6" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", - "license": "MIT/X11", - "dependencies": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "license": "MIT", - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "license": "MIT" - }, - "node_modules/parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha512-B3Nrjw2aL7aI4TDujOzfA4NsEc4u1lVcIRE0xesutH8kjeWF70uk+W5cBlIQx04zUH9NTBvuN36Y9xLRPK6Jjw==", - "license": "MIT", - "dependencies": { - "better-assert": "~1.0.0" - } - }, - "node_modules/parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha512-ijhdxJu6l5Ru12jF0JvzXVPvsC+VibqeaExlNoMhWN6VQ79PGjkmc7oA4W1lp00sFkNyj0fx6ivPLdV51/UMog==", - "license": "MIT", - "dependencies": { - "better-assert": "~1.0.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "license": "MIT" - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", - "license": "MIT" - }, - "node_modules/please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver-compare": "^1.0.0" - } - }, - "node_modules/popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", - "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "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.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-browser-comments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "license": "CC0-1.0", - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "browserslist": ">=4", - "postcss": ">=8" - } - }, - "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-colormin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", - "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-custom-properties": { - "version": "12.1.11", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", - "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "license": "MIT", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "license": "MIT", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "license": "MIT", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "license": "MIT", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-flexbugs-fixes": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.1.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "license": "CC0-1.0", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "license": "CC0-1.0", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-merge-rules": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", - "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", - "license": "MIT", - "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-normalize": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", - "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/normalize.css": "*", - "postcss-browser-comments": "^4", - "sanitize.css": "*" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "browserslist": ">= 4", - "postcss": ">= 8" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "license": "MIT", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", - "license": "MIT", - "dependencies": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "license": "MIT", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", - "license": "MIT", - "dependencies": { - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", - "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.1.1", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.13", - "browserslist": "^4.21.4", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.1.0", - "postcss-attribute-case-insensitive": "^5.0.2", - "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-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.10", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "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-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.2.0", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/postcss-svgo/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/postcss-svgo/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "license": "CC0-1.0" - }, - "node_modules/postcss-svgo/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-svgo/node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "license": "MIT", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prebuild-install/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/prebuild-install/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prebuild-install/node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/prebuild-install/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "license": "MIT", - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/protobufjs": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", - "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "license": "ISC" - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "license": "MIT", - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "license": "MIT", - "dependencies": { - "performance-now": "^2.1.0" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rdf-canonize": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.4.0.tgz", - "integrity": "sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==", - "license": "BSD-3-Clause", - "dependencies": { - "setimmediate": "^1.0.5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "license": "MIT", - "dependencies": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-app-polyfill/node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT" - }, - "node_modules/react-audio-analyser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/react-audio-analyser/-/react-audio-analyser-1.0.0.tgz", - "integrity": "sha512-wFxqgIhrjkohNWxdzN3uD0s7tCZRiGWIUmE4hzR0goWnY0NRmPvJkKZuXTU48WIvewVYe4ZksmPGyaFLAuO86Q==", - "license": "MIT" - }, - "node_modules/react-audio-player": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-audio-player/-/react-audio-player-0.17.0.tgz", - "integrity": "sha512-aCZgusPxA9HK7rLZcTdhTbBH9l6do9vn3NorgoDZRxRxJlOy9uZWzPaKjd7QdcuP2vXpxGA/61JMnnOEY7NXeA==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/react-confetti": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz", - "integrity": "sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==", - "license": "MIT", - "dependencies": { - "tween-functions": "^1.2.0" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "react": "^16.3.0 || ^17.0.1 || ^18.0.0" - } - }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/react-dev-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-dev-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/react-dev-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/react-dev-utils/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", - "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", - "license": "MIT" - }, - "node_modules/react-floater": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz", - "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==", - "license": "MIT", - "dependencies": { - "deepmerge": "^4.3.1", - "is-lite": "^0.8.2", - "popper.js": "^1.16.0", - "prop-types": "^15.8.1", - "tree-changes": "^0.9.1" - }, - "peerDependencies": { - "react": "15 - 18", - "react-dom": "15 - 18" - } - }, - "node_modules/react-floater/node_modules/@gilbarbara/deep-equal": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz", - "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==", - "license": "MIT" - }, - "node_modules/react-floater/node_modules/is-lite": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz", - "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==", - "license": "MIT" - }, - "node_modules/react-floater/node_modules/tree-changes": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz", - "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==", - "license": "MIT", - "dependencies": { - "@gilbarbara/deep-equal": "^0.1.1", - "is-lite": "^0.8.2" - } - }, - "node_modules/react-infinite-scroll-component": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", - "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", - "license": "MIT", - "dependencies": { - "throttle-debounce": "^2.1.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/react-innertext": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz", - "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==", - "license": "MIT", - "peerDependencies": { - "@types/react": ">=0.0.0 <=99", - "react": ">=0.0.0 <=99" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "license": "MIT" - }, - "node_modules/react-joyride": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.9.3.tgz", - "integrity": "sha512-1+Mg34XK5zaqJ63eeBhqdbk7dlGCFp36FXwsEvgpjqrtyywX2C6h9vr3jgxP0bGHCw8Ilsp/nRDzNVq6HJ3rNw==", - "license": "MIT", - "dependencies": { - "@gilbarbara/deep-equal": "^0.3.1", - "deep-diff": "^1.0.2", - "deepmerge": "^4.3.1", - "is-lite": "^1.2.1", - "react-floater": "^0.7.9", - "react-innertext": "^1.1.5", - "react-is": "^16.13.1", - "scroll": "^3.0.1", - "scrollparent": "^2.1.0", - "tree-changes": "^0.11.2", - "type-fest": "^4.27.0" - }, - "peerDependencies": { - "react": "15 - 18", - "react-dom": "15 - 18" - } - }, - "node_modules/react-joyride/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/react-joyride/node_modules/type-fest": { - "version": "4.39.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz", - "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", - "license": "MIT" - }, - "node_modules/react-redux": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", - "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", - "license": "MIT", - "dependencies": { - "@types/use-sync-external-store": "^0.0.3", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25", - "react": "^18.0", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", - "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.3.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", - "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.3.2", - "react-router": "6.8.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-scripts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "bin": { - "react-scripts": "bin/react-scripts.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - }, - "peerDependencies": { - "react": ">= 16", - "typescript": "^3.2.1 || ^4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/react-scripts/node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/react-scripts/node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/react-scripts/node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "license": "BSD-3-Clause" - }, - "node_modules/react-scripts/node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/react-scripts/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/react-scripts/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/react-scripts/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/react-scripts/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-scripts/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/react-scripts/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/react-scripts/node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/react-scripts/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/react-scripts/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/react-scripts/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/react-scripts/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/react-scripts/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/react-scripts/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-scripts/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/react-scripts/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/react-scripts/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/react-scripts/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/react-scripts/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-speech-recognition": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/react-speech-recognition/-/react-speech-recognition-3.10.0.tgz", - "integrity": "sha512-EVSr4Ik8l9urwdPiK2r0+ADrLyDDrjB0qBRdUWO+w2MfwEBrj6NuRmy1GD3x7BU/V6/hab0pl8Lupen0zwlJyw==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/react-virtualized": { - "version": "9.22.5", - "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.5.tgz", - "integrity": "sha512-YqQMRzlVANBv1L/7r63OHa2b0ZsAaDp1UhVNEdUaXI8A5u6hTpA5NYtUueLH2rFuY/27mTGIBl7ZhqFKzw18YQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.7.2", - "clsx": "^1.0.4", - "dom-helpers": "^5.1.3", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-lifecycles-compat": "^3.0.4" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-virtualized-auto-sizer": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.21.tgz", - "integrity": "sha512-RedZxj452+ITLfqIrR02BjvCaXV63YVIcVrvmruDZXFpJGazg4gHNs1AShPGVLvEuLGZdZ9AtkGKhWvzEujL8g==", - "license": "MIT", - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-virtualized/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/react-window": { - "version": "1.8.10", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", - "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.0.0", - "memoize-one": ">=3.1.1 <6" - }, - "engines": { - "node": ">8.0.0" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-window/node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT" - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/readdirp/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readdirp/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readdirp/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/readdirp/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readdirp/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readdirp/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "license": "MIT", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/recordrtc": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/recordrtc/-/recordrtc-5.6.2.tgz", - "integrity": "sha512-1QNKKNtl7+KcwD1lyOgP3ZlbiJ1d0HtXnypUy7yq49xEERxk31PHvE9RCciDrulPCY7WJ+oz0R9hpNxgsIurGQ==", - "license": "MIT" - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/redux-saga": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.3.0.tgz", - "integrity": "sha512-J9RvCeAZXSTAibFY0kGw6Iy4EdyDNW7k6Q+liwX+bsck7QVsU78zz8vpBRweEfANxnnlG/xGGeOvf6r8UXzNJQ==", - "license": "MIT", - "dependencies": { - "@redux-saga/core": "^1.3.0" - } - }, - "node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", - "license": "MIT", - "peerDependencies": { - "redux": "^5.0.0" - } - }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0" - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-not/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-not/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", - "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "license": "MIT", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "license": "ISC" - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/reselect": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "license": "MIT" - }, - "node_modules/resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "license": "MIT", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=8.9" - }, - "peerDependencies": { - "rework": "1.0.1", - "rework-visit": "1.0.0" - }, - "peerDependenciesMeta": { - "rework": { - "optional": true - }, - "rework-visit": { - "optional": true - } - } - }, - "node_modules/resolve-url-loader/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "license": "ISC" - }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "license": "MIT", - "engines": { - "node": ">=0.12" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", - "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", - "license": "MIT", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup-plugin-terser/node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", - "license": "MIT", - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", - "license": "CC0-1.0" - }, - "node_modules/sass": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", - "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", - "license": "MIT", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", - "license": "MIT", - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - } - } - }, - "node_modules/sass/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/sass/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/sass/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/sass/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/sass/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/sass/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "license": "ISC" - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/scroll": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", - "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==", - "license": "MIT" - }, - "node_modules/scrollparent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz", - "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==", - "license": "ISC" - }, - "node_modules/security-context": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/security-context/-/security-context-4.0.0.tgz", - "integrity": "sha512-yiDCS7tpKQl6p4NG57BdKLTSNLFfj5HosBIzXBl4jZf/qorJzSzbEUIdLhN+vVYgyLlvjixY8DPPTgqI8zvNCA==" - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "license": "MIT" - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "license": "MIT", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/selfsigned/node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", - "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "license": "ISC" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "license": "ISC" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.32.6", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", - "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.2", - "node-addon-api": "^6.1.0", - "prebuild-install": "^7.1.1", - "semver": "^7.5.4", - "simple-get": "^4.0.1", - "tar-fs": "^3.0.4", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "license": "MIT", - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "license": "MIT", - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "license": "MIT", - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/socket.io": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", - "license": "MIT", - "dependencies": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", - "license": "MIT" - }, - "node_modules/socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", - "license": "MIT", - "dependencies": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - } - }, - "node_modules/socket.io-client/node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==", - "license": "MIT" - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/socket.io-client/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/socket.io-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", - "license": "MIT", - "dependencies": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - } - }, - "node_modules/socket.io-parser/node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==", - "license": "MIT" - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==", - "license": "MIT" - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/socket.io/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sockjs/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/sodium-native": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.4.1.tgz", - "integrity": "sha512-PaNN/roiFWzVVTL6OqjzYct38NSXewdl2wz8SRB51Br/MLIJPrbM3XexhVWkq7D3UWMysfrhKVf1v1phZq6MeQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "license": "MIT" - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", - "license": "MIT", - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/source-map-loader/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "license": "MIT", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "license": "MIT" - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "license": "MIT" - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/spdy-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/split-graphemes": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/split-graphemes/-/split-graphemes-0.5.0.tgz", - "integrity": "sha512-TTZ4IPmxuGZOPqhKLHscOeVf568gDP8JE8FxaZU/k30Q3Ra1BRhMv8d9jq1R6A/6T8wSz6ozYn3aJ5skgP6omA==", - "license": "MIT" - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-string/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-string/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", - "license": "MIT" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "license": "MIT" - }, - "node_modules/static-eval": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", - "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", - "license": "MIT", - "dependencies": { - "escodegen": "^1.8.1" - } - }, - "node_modules/static-eval/node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/static-eval/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/static-eval/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-eval/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-eval/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-eval/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-eval/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", - "license": "MIT", - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "license": "MIT", - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/streamroller": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", - "deprecated": "0.x is no longer supported. Please upgrade to 3.x or higher.", - "license": "MIT", - "dependencies": { - "date-format": "^1.2.0", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "readable-stream": "^2.3.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/streamroller/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "license": "BSD-2-Clause", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "license": "MIT" - }, - "node_modules/style-loader": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", - "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", - "license": "MIT" - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "license": "MIT" - }, - "node_modules/svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", - "license": "MIT", - "dependencies": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "node_modules/svgo/node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "license": "BSD-2-Clause" - }, - "node_modules/svgo/node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "license": "MIT" - }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/table/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/table/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.14", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", - "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tailwindcss/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/tailwindcss/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-fs": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.9.tgz", - "integrity": "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", - "license": "MIT", - "dependencies": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.36.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", - "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "license": "MIT" - }, - "node_modules/throttle-debounce": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", - "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "license": "MIT" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "license": "BSD-3-Clause" - }, - "node_modules/to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "license": "MIT", - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/to-regex/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tree-changes": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.3.tgz", - "integrity": "sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag==", - "license": "MIT", - "dependencies": { - "@gilbarbara/deep-equal": "^0.3.1", - "is-lite": "^1.2.1" - } - }, - "node_modules/tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "license": "MIT" - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ts-node/node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "license": "MIT" - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", - "license": "0BSD" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tween-functions": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", - "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==", - "license": "BSD" - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "license": "Unlicense" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-compare": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", - "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", - "license": "MIT", - "dependencies": { - "typescript-logic": "^0.0.0" - } - }, - "node_modules/typescript-logic": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", - "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==", - "license": "MIT" - }, - "node_modules/typescript-tuple": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", - "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", - "license": "MIT", - "dependencies": { - "typescript-compare": "^0.0.2" - } - }, - "node_modules/ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "license": "MIT" - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "license": "MIT", - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "license": "MIT", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", - "license": "MIT" - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", - "license": "MIT", - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", - "license": "MIT", - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "license": "MIT", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "license": "MIT", - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "license": "MIT" - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/use-sound": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/use-sound/-/use-sound-4.0.3.tgz", - "integrity": "sha512-L205pEUFIrLsGYsCUKHQVCt0ajs//YQOFbEQeNwaWaqQj3y3st4SuR+rvpMHLmv8hgTcfUFlvMQawZNI3OE18w==", - "license": "MIT", - "dependencies": { - "howler": "^2.1.3" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "license": "MIT", - "dependencies": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" - } - }, - "node_modules/useragent/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "license": "ISC", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/useragent/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "license": "ISC" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "license": "ISC", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT" - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "license": "MIT", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "license": "MIT", - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-vitals": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", - "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==", - "license": "Apache-2.0" - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=10.4" - } - }, - "node_modules/webpack": { - "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT" - }, - "node_modules/webpack-dev-server": { - "version": "4.15.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", - "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.4", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/webpack-dev-server/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/webpack-dev-server/node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "license": "MIT", - "dependencies": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "webpack": "^4.44.2 || ^5.47.0" - } - }, - "node_modules/webpack-manifest-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "license": "MIT", - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT" - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "license": "MIT" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/whatwg-url/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "license": "MIT", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "license": "MIT", - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "license": "MIT", - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/workbox-background-sync": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", - "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", - "license": "MIT", - "dependencies": { - "idb": "^7.0.1", - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-broadcast-update": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", - "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-build": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", - "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", - "license": "MIT", - "dependencies": { - "@apideck/better-ajv-errors": "^0.3.1", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "@babel/runtime": "^7.11.2", - "@rollup/plugin-babel": "^5.2.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-replace": "^2.4.1", - "@surma/rollup-plugin-off-main-thread": "^2.2.3", - "ajv": "^8.6.0", - "common-tags": "^1.8.0", - "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "lodash": "^4.17.20", - "pretty-bytes": "^5.3.0", - "rollup": "^2.43.1", - "rollup-plugin-terser": "^7.0.0", - "source-map": "^0.8.0-beta.0", - "stringify-object": "^3.3.0", - "strip-comments": "^2.0.1", - "tempy": "^0.6.0", - "upath": "^1.2.0", - "workbox-background-sync": "6.6.0", - "workbox-broadcast-update": "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-window": "6.6.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/workbox-build/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/workbox-build/node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "license": "BSD-3-Clause", - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workbox-build/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "license": "MIT", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/workbox-build/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "license": "BSD-2-Clause" - }, - "node_modules/workbox-build/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "license": "MIT", - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/workbox-cacheable-response": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", - "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", - "deprecated": "workbox-background-sync@6.6.0", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-core": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", - "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==", - "license": "MIT" - }, - "node_modules/workbox-expiration": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", - "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", - "license": "MIT", - "dependencies": { - "idb": "^7.0.1", - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-google-analytics": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", - "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", - "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", - "license": "MIT", - "dependencies": { - "workbox-background-sync": "6.6.0", - "workbox-core": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0" - } - }, - "node_modules/workbox-navigation-preload": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", - "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-precaching": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", - "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0" - } - }, - "node_modules/workbox-range-requests": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", - "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-recipes": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", - "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", - "license": "MIT", - "dependencies": { - "workbox-cacheable-response": "6.6.0", - "workbox-core": "6.6.0", - "workbox-expiration": "6.6.0", - "workbox-precaching": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0" - } - }, - "node_modules/workbox-routing": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", - "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-strategies": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", - "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-streams": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", - "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", - "license": "MIT", - "dependencies": { - "workbox-core": "6.6.0", - "workbox-routing": "6.6.0" - } - }, - "node_modules/workbox-sw": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", - "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==", - "license": "MIT" - }, - "node_modules/workbox-webpack-plugin": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", - "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "^2.1.0", - "pretty-bytes": "^5.4.1", - "upath": "^1.2.0", - "webpack-sources": "^1.4.3", - "workbox-build": "6.6.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": "^4.4.0 || ^5.9.0" - } - }, - "node_modules/workbox-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "license": "MIT", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/workbox-window": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", - "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", - "license": "MIT", - "dependencies": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.6.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "license": "Apache-2.0" - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "license": "MIT" - }, - "node_modules/xmldom": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.19.tgz", - "integrity": "sha512-pDyxjQSFQgNHkU+yjvoF+GXVGJU7e9EnOg/KcGMDihBIKjTsOeDYaECwC/O9bsUWKY+Sd9izfE43JXC46EOHKA==", - "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0", - "engines": { - "node": ">=0.1" - } - }, - "node_modules/xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha512-/bFPLUgJrfGUL10AIv4Y7/CUt6so9CLtB/oFxQSHseSDNNCdC6vwwKEqwLN6wNPBg9YWXAiMu8jkf6RPRS/75Q==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==", - "license": "MIT" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index 9d0e2e71..65958ada 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,9 @@ "@mui/styles": "^5.2.3", "@number-flow/react": "^0.5.10", "@project-sunbird/client-services": "^5.1.2", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tooltip": "^1.2.8", "@reduxjs/toolkit": "^2.2.0", "@tekdi/all-telemetry-sdk": "^0.0.32", "@tekdi/multilingual-profanity-filter": "1.1.0", @@ -26,7 +29,9 @@ "axios": "^1.9.0", "canvas-confetti": "^1.9.2", "character-error-rate": "^1.1.4", + "class-variance-authority": "^0.7.1", "classnames": "^2.3.1", + "clsx": "^2.1.1", "crypto-js": "^4.2.0", "double-metaphone": "^2.0.1", "eslint-plugin-import": "^2.28.0", @@ -60,12 +65,13 @@ "redux-saga": "^1.1.3", "sass": "^1.44.0", "split-graphemes": "^0.5.0", + "tailwind-merge": "^3.4.0", "use-sound": "^4.0.1", "web-vitals": "^2.1.4" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", + "start": "NODE_OPTIONS=--max-old-space-size=4096 react-app-rewired start", + "build": "NODE_OPTIONS=--max-old-space-size=4096 react-app-rewired build", "test": "react-scripts test", "eject": "react-scripts eject", "prettier:cli": "prettier \"src/**/*.js\" \"**/*.json\"", @@ -102,14 +108,21 @@ ] }, "devDependencies": { + "@babel/preset-typescript": "^7.28.5", "@mui/styles": "^5.15.10", + "autoprefixer": "^10.4.23", + "customize-cra": "^1.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^3.4.0", "husky": "^9.0.11", "lint-staged": "^11.0.0", + "postcss": "^8.5.6", "prettier": "^2.3.2", - "react": "^18.2.0" + "react": "^18.2.0", + "react-app-rewired": "^2.2.1", + "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7" }, "lint-staged": { "src/**/*.{js,jsx}": [ diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..426f298d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,11 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} + + + + + diff --git a/public/audio/audio-preview/Alphabet Chart/Chart Icon/en/ChartNarration.wav b/public/audio/audio-preview/Alphabet Chart/Chart Icon/en/ChartNarration.wav new file mode 100644 index 00000000..03c897b3 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/Chart Icon/en/ChartNarration.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/Chart Icon/kn/ChartNarration.wav b/public/audio/audio-preview/Alphabet Chart/Chart Icon/kn/ChartNarration.wav new file mode 100644 index 00000000..b0a5ed56 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/Chart Icon/kn/ChartNarration.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/Chart Icon/te/ChartNarration.wav b/public/audio/audio-preview/Alphabet Chart/Chart Icon/te/ChartNarration.wav new file mode 100644 index 00000000..c3414dee Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/Chart Icon/te/ChartNarration.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration1.wav b/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration1.wav new file mode 100644 index 00000000..016bfeab Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration1.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration2.wav b/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration2.wav new file mode 100644 index 00000000..afa83fb5 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration2.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration3.wav b/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration3.wav new file mode 100644 index 00000000..6a4e3e62 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/en/alphabetNarration3.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/en/syllableNarration1.wav b/public/audio/audio-preview/Alphabet Chart/en/syllableNarration1.wav new file mode 100644 index 00000000..91716ae5 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/en/syllableNarration1.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/en/syllableNarration2.wav b/public/audio/audio-preview/Alphabet Chart/en/syllableNarration2.wav new file mode 100644 index 00000000..5d21ed7d Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/en/syllableNarration2.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/en/syllableNarration3.wav b/public/audio/audio-preview/Alphabet Chart/en/syllableNarration3.wav new file mode 100644 index 00000000..e6e24d5f Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/en/syllableNarration3.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration1.wav b/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration1.wav new file mode 100644 index 00000000..4063ee4d Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration1.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration2.wav b/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration2.wav new file mode 100644 index 00000000..9eed16eb Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration2.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration3.wav b/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration3.wav new file mode 100644 index 00000000..e9308845 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/kn/alphabetNarration3.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration1.wav b/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration1.wav new file mode 100644 index 00000000..96fdf7c7 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration1.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration2.wav b/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration2.wav new file mode 100644 index 00000000..7e74fe63 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration2.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration3.wav b/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration3.wav new file mode 100644 index 00000000..292f5bb9 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/kn/syllableNarration3.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration1.wav b/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration1.wav new file mode 100644 index 00000000..e3f77f55 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration1.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration2.wav b/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration2.wav new file mode 100644 index 00000000..6bff1c03 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration2.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration3.wav b/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration3.wav new file mode 100644 index 00000000..1aa15800 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/te/alphabetNarration3.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/te/syllableNarration1.wav b/public/audio/audio-preview/Alphabet Chart/te/syllableNarration1.wav new file mode 100644 index 00000000..cfbe4cb0 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/te/syllableNarration1.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/te/syllableNarration2.wav b/public/audio/audio-preview/Alphabet Chart/te/syllableNarration2.wav new file mode 100644 index 00000000..08e6b0a3 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/te/syllableNarration2.wav differ diff --git a/public/audio/audio-preview/Alphabet Chart/te/syllableNarration3.wav b/public/audio/audio-preview/Alphabet Chart/te/syllableNarration3.wav new file mode 100644 index 00000000..efeebb95 Binary files /dev/null and b/public/audio/audio-preview/Alphabet Chart/te/syllableNarration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/introduction/en/introduction.wav b/public/audio/audio-preview/combined-letter-games/introduction/en/introduction.wav new file mode 100644 index 00000000..ddd312d7 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/introduction/en/introduction.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/introduction/hi/introduction.wav b/public/audio/audio-preview/combined-letter-games/introduction/hi/introduction.wav new file mode 100644 index 00000000..0bbf9fea Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/introduction/hi/introduction.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/introduction/kn/introduction.wav b/public/audio/audio-preview/combined-letter-games/introduction/kn/introduction.wav new file mode 100644 index 00000000..3f6f3c15 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/introduction/kn/introduction.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/introduction/mr/introduction.wav b/public/audio/audio-preview/combined-letter-games/introduction/mr/introduction.wav new file mode 100644 index 00000000..70371520 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/introduction/mr/introduction.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/introduction/te/introduction.wav b/public/audio/audio-preview/combined-letter-games/introduction/te/introduction.wav new file mode 100644 index 00000000..bbddb267 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/introduction/te/introduction.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration1.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration1.wav new file mode 100644 index 00000000..d2474d54 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration2.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration2.wav new file mode 100644 index 00000000..647b516c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration3.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration3.wav new file mode 100644 index 00000000..1a873a81 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration4.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration4.wav new file mode 100644 index 00000000..2a196eb4 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration1.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration1.wav new file mode 100644 index 00000000..1297820c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration2.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration2.wav new file mode 100644 index 00000000..f8b0fbc7 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration3.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration3.wav new file mode 100644 index 00000000..76ce7a9c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration4.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration4.wav new file mode 100644 index 00000000..70c67956 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration1.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration1.wav new file mode 100644 index 00000000..e00ef191 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration2.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration2.wav new file mode 100644 index 00000000..399c9e66 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration3.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration3.wav new file mode 100644 index 00000000..78403737 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration4.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration4.wav new file mode 100644 index 00000000..3ff55792 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration1.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration1.wav new file mode 100644 index 00000000..e1c724bd Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration2.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration2.wav new file mode 100644 index 00000000..b7aae25c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration3.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration3.wav new file mode 100644 index 00000000..c88fa819 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration4.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration4.wav new file mode 100644 index 00000000..72d240e7 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration1.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration1.wav new file mode 100644 index 00000000..2e3f9d4c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration2.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration2.wav new file mode 100644 index 00000000..dddd2f9a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration3.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration3.wav new file mode 100644 index 00000000..783337ea Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration4.wav b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration4.wav new file mode 100644 index 00000000..9172a1a7 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/all_ready.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/all_ready.wav new file mode 100644 index 00000000..b41892d8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/all_ready.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/amazing.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/amazing.wav new file mode 100644 index 00000000..c8d10417 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/amazing.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/correct_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/correct_feedback.wav new file mode 100644 index 00000000..9684feaa Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/correct_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_explanation.wav new file mode 100644 index 00000000..179e9a8d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_meter.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_meter.wav new file mode 100644 index 00000000..5d11b782 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_meter.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_requirement.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_requirement.wav new file mode 100644 index 00000000..fae0d297 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_requirement.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_rilo.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_rilo.wav new file mode 100644 index 00000000..ed802ec3 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/fuel_rilo.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/game_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/game_explanation.wav new file mode 100644 index 00000000..e7409e4d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/game_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/great_job.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/great_job.wav new file mode 100644 index 00000000..ddd00834 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/great_job.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_destination.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_destination.wav new file mode 100644 index 00000000..96acd1d1 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_destination.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_narrator.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_narrator.wav new file mode 100644 index 00000000..1821de7c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_narrator.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_rocket.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_rocket.wav new file mode 100644 index 00000000..b3b28aca Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/intro_rocket.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_controls.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_controls.wav new file mode 100644 index 00000000..04f369e1 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_controls.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_intro.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_intro.wav new file mode 100644 index 00000000..b565095c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_intro.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_question.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_question.wav new file mode 100644 index 00000000..dfe888f4 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/practice_question.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/ready_to_start.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/ready_to_start.wav new file mode 100644 index 00000000..00674d0b Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/ready_to_start.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/rilo_appears.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/rilo_appears.wav new file mode 100644 index 00000000..b66a689f Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/rilo_appears.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/wrong_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/wrong_feedback.wav new file mode 100644 index 00000000..3ac680b6 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/en/wrong_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/all_ready.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/all_ready.wav new file mode 100644 index 00000000..17f516a3 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/all_ready.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/amazing.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/amazing.wav new file mode 100644 index 00000000..a48aa582 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/amazing.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/correct_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/correct_feedback.wav new file mode 100644 index 00000000..3b421d70 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/correct_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_explanation.wav new file mode 100644 index 00000000..62cf7292 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_meter.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_meter.wav new file mode 100644 index 00000000..f94024b3 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_meter.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_requirement.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_requirement.wav new file mode 100644 index 00000000..61fad6fc Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_requirement.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_rilo.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_rilo.wav new file mode 100644 index 00000000..39170933 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/fuel_rilo.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/game_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/game_explanation.wav new file mode 100644 index 00000000..8a1f395e Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/game_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/great_job.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/great_job.wav new file mode 100644 index 00000000..ae17402e Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/great_job.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_destination.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_destination.wav new file mode 100644 index 00000000..18e30156 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_destination.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_narrator.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_narrator.wav new file mode 100644 index 00000000..280930f2 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_narrator.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_rocket.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_rocket.wav new file mode 100644 index 00000000..61f7e923 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/intro_rocket.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_controls.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_controls.wav new file mode 100644 index 00000000..25507641 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_controls.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_intro.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_intro.wav new file mode 100644 index 00000000..3f4d2512 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_intro.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_question.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_question.wav new file mode 100644 index 00000000..e47323fe Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/practice_question.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/ready_to_start.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/ready_to_start.wav new file mode 100644 index 00000000..65b082f8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/ready_to_start.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/rilo_appears.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/rilo_appears.wav new file mode 100644 index 00000000..b6070de6 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/rilo_appears.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/wrong_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/wrong_feedback.wav new file mode 100644 index 00000000..bd426081 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/hi/wrong_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/all_ready.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/all_ready.wav new file mode 100644 index 00000000..125d4c06 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/all_ready.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/amazing.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/amazing.wav new file mode 100644 index 00000000..8424a9e5 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/amazing.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/correct_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/correct_feedback.wav new file mode 100644 index 00000000..d17dd5fc Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/correct_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_explanation.wav new file mode 100644 index 00000000..598a0208 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_meter.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_meter.wav new file mode 100644 index 00000000..dc3f80ff Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_meter.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_requirement.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_requirement.wav new file mode 100644 index 00000000..68e41b3e Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_requirement.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_rilo.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_rilo.wav new file mode 100644 index 00000000..c5d01e51 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/fuel_rilo.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/game_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/game_explanation.wav new file mode 100644 index 00000000..7d6896f8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/game_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/great_job.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/great_job.wav new file mode 100644 index 00000000..c6fac567 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/great_job.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_destination.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_destination.wav new file mode 100644 index 00000000..14e747bb Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_destination.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_narrator.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_narrator.wav new file mode 100644 index 00000000..513e58a1 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_narrator.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_rocket.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_rocket.wav new file mode 100644 index 00000000..7f3e1c48 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/intro_rocket.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_controls.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_controls.wav new file mode 100644 index 00000000..8d89b436 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_controls.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_intro.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_intro.wav new file mode 100644 index 00000000..6ea59bcb Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_intro.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_question.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_question.wav new file mode 100644 index 00000000..6cd60d2d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/practice_question.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/ready_to_start.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/ready_to_start.wav new file mode 100644 index 00000000..8915db53 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/ready_to_start.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/rilo_appears.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/rilo_appears.wav new file mode 100644 index 00000000..da42af40 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/rilo_appears.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/wrong_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/wrong_feedback.wav new file mode 100644 index 00000000..3b1b1dbd Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/kn/wrong_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/all_ready.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/all_ready.wav new file mode 100644 index 00000000..d5c7ea28 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/all_ready.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/amazing.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/amazing.wav new file mode 100644 index 00000000..60a93b81 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/amazing.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/correct_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/correct_feedback.wav new file mode 100644 index 00000000..a16df04a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/correct_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_explanation.wav new file mode 100644 index 00000000..9fdaf90c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_meter.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_meter.wav new file mode 100644 index 00000000..43cac4d8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_meter.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_requirement.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_requirement.wav new file mode 100644 index 00000000..48c2c20a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_requirement.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_rilo.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_rilo.wav new file mode 100644 index 00000000..fe4d97e8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/fuel_rilo.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/game_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/game_explanation.wav new file mode 100644 index 00000000..51c41330 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/game_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/great_job.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/great_job.wav new file mode 100644 index 00000000..4119394d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/great_job.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_destination.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_destination.wav new file mode 100644 index 00000000..21dc079e Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_destination.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_narrator.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_narrator.wav new file mode 100644 index 00000000..ed4fc611 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_narrator.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_rocket.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_rocket.wav new file mode 100644 index 00000000..1ecd89c9 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/intro_rocket.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_controls.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_controls.wav new file mode 100644 index 00000000..c2b9bb54 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_controls.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_intro.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_intro.wav new file mode 100644 index 00000000..0598e08c Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_intro.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_question.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_question.wav new file mode 100644 index 00000000..c2844ae2 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/practice_question.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/ready_to_start.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/ready_to_start.wav new file mode 100644 index 00000000..71640664 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/ready_to_start.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/rilo_appears.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/rilo_appears.wav new file mode 100644 index 00000000..ca049a47 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/rilo_appears.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/wrong_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/wrong_feedback.wav new file mode 100644 index 00000000..5d1643b6 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/mr/wrong_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/all_ready.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/all_ready.wav new file mode 100644 index 00000000..034417a0 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/all_ready.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/amazing.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/amazing.wav new file mode 100644 index 00000000..99d4456a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/amazing.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/correct_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/correct_feedback.wav new file mode 100644 index 00000000..135c3036 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/correct_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_explanation.wav new file mode 100644 index 00000000..12e24824 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_meter.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_meter.wav new file mode 100644 index 00000000..e4747a85 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_meter.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_requirement.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_requirement.wav new file mode 100644 index 00000000..21fcb432 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_requirement.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_rilo.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_rilo.wav new file mode 100644 index 00000000..58ade5fb Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/fuel_rilo.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/game_explanation.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/game_explanation.wav new file mode 100644 index 00000000..2f2cf201 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/game_explanation.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/great_job.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/great_job.wav new file mode 100644 index 00000000..287f09c4 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/great_job.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_destination.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_destination.wav new file mode 100644 index 00000000..797a7f51 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_destination.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_narrator.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_narrator.wav new file mode 100644 index 00000000..27ca8ae8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_narrator.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_rocket.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_rocket.wav new file mode 100644 index 00000000..b7c0a782 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/intro_rocket.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_controls.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_controls.wav new file mode 100644 index 00000000..dd2bced9 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_controls.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_intro.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_intro.wav new file mode 100644 index 00000000..3fff5c4d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_intro.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_question.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_question.wav new file mode 100644 index 00000000..adacc75d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/practice_question.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/ready_to_start.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/ready_to_start.wav new file mode 100644 index 00000000..e70d1d8d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/ready_to_start.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/rilo_appears.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/rilo_appears.wav new file mode 100644 index 00000000..f8638b9a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/rilo_appears.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/wrong_feedback.wav b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/wrong_feedback.wav new file mode 100644 index 00000000..a3265a67 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/letter-launcher-story/te/wrong_feedback.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration1.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration1.wav new file mode 100644 index 00000000..5f859023 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration2.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration2.wav new file mode 100644 index 00000000..b040c74a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration3.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration3.wav new file mode 100644 index 00000000..4ea3637b Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration4.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration4.wav new file mode 100644 index 00000000..3a3c857b Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration1.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration1.wav new file mode 100644 index 00000000..15f90ae8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration2.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration2.wav new file mode 100644 index 00000000..6965c4fa Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration3.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration3.wav new file mode 100644 index 00000000..797421d6 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration4.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration4.wav new file mode 100644 index 00000000..f95402e2 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration1.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration1.wav new file mode 100644 index 00000000..312e7fc8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration2.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration2.wav new file mode 100644 index 00000000..fb57ccfc Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration3.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration3.wav new file mode 100644 index 00000000..eb569522 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration4.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration4.wav new file mode 100644 index 00000000..45d55c8a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration1.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration1.wav new file mode 100644 index 00000000..a21bef5d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration2.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration2.wav new file mode 100644 index 00000000..1a0d7952 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration3.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration3.wav new file mode 100644 index 00000000..dfc047f8 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration4.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration4.wav new file mode 100644 index 00000000..8613e22a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration1.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration1.wav new file mode 100644 index 00000000..ca83ac07 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration2.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration2.wav new file mode 100644 index 00000000..67a2fe8f Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration3.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration3.wav new file mode 100644 index 00000000..faf1d580 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration4.wav b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration4.wav new file mode 100644 index 00000000..d5d4fbd2 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration1.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration1.wav new file mode 100644 index 00000000..db6e3f9f Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration2.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration2.wav new file mode 100644 index 00000000..3fa339fb Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration3.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration3.wav new file mode 100644 index 00000000..b762dbbe Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration4.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration4.wav new file mode 100644 index 00000000..6a889c4a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration1.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration1.wav new file mode 100644 index 00000000..9201368a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration2.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration2.wav new file mode 100644 index 00000000..c5e716f7 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration3.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration3.wav new file mode 100644 index 00000000..7f0282fb Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration4.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration4.wav new file mode 100644 index 00000000..e843db06 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration1.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration1.wav new file mode 100644 index 00000000..c91f934e Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration2.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration2.wav new file mode 100644 index 00000000..936cf57d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration3.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration3.wav new file mode 100644 index 00000000..33604e51 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration4.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration4.wav new file mode 100644 index 00000000..19446352 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration1.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration1.wav new file mode 100644 index 00000000..2a5a192a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration2.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration2.wav new file mode 100644 index 00000000..e1675d2b Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration3.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration3.wav new file mode 100644 index 00000000..1ef2360a Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration4.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration4.wav new file mode 100644 index 00000000..74057904 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration1.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration1.wav new file mode 100644 index 00000000..edea6f93 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration2.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration2.wav new file mode 100644 index 00000000..bd7b577d Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration3.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration3.wav new file mode 100644 index 00000000..d71ebfc4 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration4.wav b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration4.wav new file mode 100644 index 00000000..afab0099 Binary files /dev/null and b/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration1.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration1.wav new file mode 100644 index 00000000..a542bebe Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration2.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration2.wav new file mode 100644 index 00000000..37d2b70f Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration3.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration3.wav new file mode 100644 index 00000000..a7fc32b8 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration4.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration4.wav new file mode 100644 index 00000000..0d124281 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration1.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration1.wav new file mode 100644 index 00000000..0ff14555 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration2.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration2.wav new file mode 100644 index 00000000..1943d4df Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration3.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration3.wav new file mode 100644 index 00000000..111e21c9 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration4.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration4.wav new file mode 100644 index 00000000..e64ac6de Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration1.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration1.wav new file mode 100644 index 00000000..cc7ba349 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration2.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration2.wav new file mode 100644 index 00000000..90de8774 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration3.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration3.wav new file mode 100644 index 00000000..8abf7853 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration4.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration4.wav new file mode 100644 index 00000000..01ed8cbf Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration1.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration1.wav new file mode 100644 index 00000000..b2e23a36 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration2.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration2.wav new file mode 100644 index 00000000..f210f4b4 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration3.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration3.wav new file mode 100644 index 00000000..7fd36d0b Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration4.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration4.wav new file mode 100644 index 00000000..6c44abd7 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration1.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration1.wav new file mode 100644 index 00000000..3c0a3d8f Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration2.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration2.wav new file mode 100644 index 00000000..19c07400 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration3.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration3.wav new file mode 100644 index 00000000..885b4854 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration4.wav b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration4.wav new file mode 100644 index 00000000..a64cc7d8 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/introduction/en/introduction.wav b/public/audio/audio-preview/combined-sentence-games/introduction/en/introduction.wav new file mode 100644 index 00000000..091753c1 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/introduction/en/introduction.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/introduction/hi/introduction.wav b/public/audio/audio-preview/combined-sentence-games/introduction/hi/introduction.wav new file mode 100644 index 00000000..5031b123 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/introduction/hi/introduction.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/introduction/kn/introduction.wav b/public/audio/audio-preview/combined-sentence-games/introduction/kn/introduction.wav new file mode 100644 index 00000000..2addf721 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/introduction/kn/introduction.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/introduction/mr/introduction.wav b/public/audio/audio-preview/combined-sentence-games/introduction/mr/introduction.wav new file mode 100644 index 00000000..26a3c8f5 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/introduction/mr/introduction.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/introduction/te/introduction.wav b/public/audio/audio-preview/combined-sentence-games/introduction/te/introduction.wav new file mode 100644 index 00000000..37a00b80 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/introduction/te/introduction.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration1.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration1.wav new file mode 100644 index 00000000..ff281f67 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration2.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration2.wav new file mode 100644 index 00000000..3a3bd689 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration3.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration3.wav new file mode 100644 index 00000000..0853b6b3 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration4.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration4.wav new file mode 100644 index 00000000..523291ef Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration1.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration1.wav new file mode 100644 index 00000000..50011d59 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration2.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration2.wav new file mode 100644 index 00000000..0dad3f04 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration3.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration3.wav new file mode 100644 index 00000000..5e35a61a Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration4.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration4.wav new file mode 100644 index 00000000..f0a419d2 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration1.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration1.wav new file mode 100644 index 00000000..ddeb269a Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration2.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration2.wav new file mode 100644 index 00000000..689f3600 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration3.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration3.wav new file mode 100644 index 00000000..b670cccd Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration4.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration4.wav new file mode 100644 index 00000000..ee5286a7 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration1.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration1.wav new file mode 100644 index 00000000..501c2215 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration2.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration2.wav new file mode 100644 index 00000000..43e42b90 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration3.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration3.wav new file mode 100644 index 00000000..ad76a52f Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration4.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration4.wav new file mode 100644 index 00000000..84002fcf Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration1.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration1.wav new file mode 100644 index 00000000..dd0c8d2c Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration2.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration2.wav new file mode 100644 index 00000000..c31731bb Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration3.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration3.wav new file mode 100644 index 00000000..e3c01712 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration4.wav b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration4.wav new file mode 100644 index 00000000..2caafd61 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration1.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration1.wav new file mode 100644 index 00000000..057c21a3 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration2.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration2.wav new file mode 100644 index 00000000..5c629bba Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration3.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration3.wav new file mode 100644 index 00000000..a2c8b9e5 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration4.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration4.wav new file mode 100644 index 00000000..0d124281 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration1.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration1.wav new file mode 100644 index 00000000..ce5bea7e Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration2.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration2.wav new file mode 100644 index 00000000..669f9517 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration3.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration3.wav new file mode 100644 index 00000000..38fae52b Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration4.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration4.wav new file mode 100644 index 00000000..e64ac6de Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration1.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration1.wav new file mode 100644 index 00000000..c5f12158 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration2.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration2.wav new file mode 100644 index 00000000..79b224d2 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration3.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration3.wav new file mode 100644 index 00000000..65870145 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration4.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration4.wav new file mode 100644 index 00000000..01ed8cbf Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration1.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration1.wav new file mode 100644 index 00000000..fd4869cc Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration2.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration2.wav new file mode 100644 index 00000000..439659ba Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration3.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration3.wav new file mode 100644 index 00000000..b935bb79 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration4.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration4.wav new file mode 100644 index 00000000..6c44abd7 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration1.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration1.wav new file mode 100644 index 00000000..e4c75f67 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration2.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration2.wav new file mode 100644 index 00000000..19c07400 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration3.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration3.wav new file mode 100644 index 00000000..bc38c251 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration4.wav b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration4.wav new file mode 100644 index 00000000..d2184bf9 Binary files /dev/null and b/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/introduction/en/introduction.wav b/public/audio/audio-preview/combined-word-games/introduction/en/introduction.wav new file mode 100644 index 00000000..dcc57e2b Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/introduction/en/introduction.wav differ diff --git a/public/audio/audio-preview/combined-word-games/introduction/hi/introduction.wav b/public/audio/audio-preview/combined-word-games/introduction/hi/introduction.wav new file mode 100644 index 00000000..57bc6923 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/introduction/hi/introduction.wav differ diff --git a/public/audio/audio-preview/combined-word-games/introduction/kn/introduction.wav b/public/audio/audio-preview/combined-word-games/introduction/kn/introduction.wav new file mode 100644 index 00000000..55e44595 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/introduction/kn/introduction.wav differ diff --git a/public/audio/audio-preview/combined-word-games/introduction/mr/introduction.wav b/public/audio/audio-preview/combined-word-games/introduction/mr/introduction.wav new file mode 100644 index 00000000..92e7ecc4 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/introduction/mr/introduction.wav differ diff --git a/public/audio/audio-preview/combined-word-games/introduction/te/introduction.wav b/public/audio/audio-preview/combined-word-games/introduction/te/introduction.wav new file mode 100644 index 00000000..c50943cc Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/introduction/te/introduction.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/en/narration1.wav b/public/audio/audio-preview/combined-word-games/picture-words/en/narration1.wav new file mode 100644 index 00000000..f3d2fb31 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/en/narration2.wav b/public/audio/audio-preview/combined-word-games/picture-words/en/narration2.wav new file mode 100644 index 00000000..03b1d1cf Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/en/narration3.wav b/public/audio/audio-preview/combined-word-games/picture-words/en/narration3.wav new file mode 100644 index 00000000..62a4fac2 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/en/narration4.wav b/public/audio/audio-preview/combined-word-games/picture-words/en/narration4.wav new file mode 100644 index 00000000..e1469177 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/hi/narration1.wav b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration1.wav new file mode 100644 index 00000000..97236764 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/hi/narration2.wav b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration2.wav new file mode 100644 index 00000000..99494616 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/hi/narration3.wav b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration3.wav new file mode 100644 index 00000000..771a0dc1 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/hi/narration4.wav b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration4.wav new file mode 100644 index 00000000..725f3485 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/kn/narration1.wav b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration1.wav new file mode 100644 index 00000000..85726faa Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/kn/narration2.wav b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration2.wav new file mode 100644 index 00000000..555b1c4a Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/kn/narration3.wav b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration3.wav new file mode 100644 index 00000000..4b8f9a10 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/kn/narration4.wav b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration4.wav new file mode 100644 index 00000000..f1a7a5ca Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/mr/narration1.wav b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration1.wav new file mode 100644 index 00000000..deade34d Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/mr/narration2.wav b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration2.wav new file mode 100644 index 00000000..12def20b Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/mr/narration3.wav b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration3.wav new file mode 100644 index 00000000..d040b7a5 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/mr/narration4.wav b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration4.wav new file mode 100644 index 00000000..6eca7af4 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/te/narration1.wav b/public/audio/audio-preview/combined-word-games/picture-words/te/narration1.wav new file mode 100644 index 00000000..2f6a2bde Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/te/narration2.wav b/public/audio/audio-preview/combined-word-games/picture-words/te/narration2.wav new file mode 100644 index 00000000..dc7c9602 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/te/narration3.wav b/public/audio/audio-preview/combined-word-games/picture-words/te/narration3.wav new file mode 100644 index 00000000..0424dddb Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/picture-words/te/narration4.wav b/public/audio/audio-preview/combined-word-games/picture-words/te/narration4.wav new file mode 100644 index 00000000..d8ed1cc4 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/picture-words/te/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/en/bread.wav b/public/audio/audio-preview/combined-word-games/sound-match/en/bread.wav new file mode 100644 index 00000000..f80586f9 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/en/bread.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/en/narration1.wav b/public/audio/audio-preview/combined-word-games/sound-match/en/narration1.wav new file mode 100644 index 00000000..4415b31c Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/en/narration2.wav b/public/audio/audio-preview/combined-word-games/sound-match/en/narration2.wav new file mode 100644 index 00000000..2f14278b Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/en/narration3.wav b/public/audio/audio-preview/combined-word-games/sound-match/en/narration3.wav new file mode 100644 index 00000000..146345f4 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/en/narration4.wav b/public/audio/audio-preview/combined-word-games/sound-match/en/narration4.wav new file mode 100644 index 00000000..3c596296 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/hi/narration1.wav b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration1.wav new file mode 100644 index 00000000..986961c0 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/hi/narration2.wav b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration2.wav new file mode 100644 index 00000000..26dd42c9 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/hi/narration3.wav b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration3.wav new file mode 100644 index 00000000..d066dad3 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/hi/narration4.wav b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration4.wav new file mode 100644 index 00000000..14582c66 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/kn/bread.wav b/public/audio/audio-preview/combined-word-games/sound-match/kn/bread.wav new file mode 100644 index 00000000..03449fdf Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/kn/bread.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/kn/narration1.wav b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration1.wav new file mode 100644 index 00000000..3aa92e85 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/kn/narration2.wav b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration2.wav new file mode 100644 index 00000000..4c67f38e Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/kn/narration3.wav b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration3.wav new file mode 100644 index 00000000..b63b5212 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/kn/narration4.wav b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration4.wav new file mode 100644 index 00000000..e087d1ba Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/kn/narration4.wav differ diff --git "a/public/audio/audio-preview/combined-word-games/sound-match/kn/\340\262\252\340\262\225\340\263\215\340\262\267\340\262\277.wav" "b/public/audio/audio-preview/combined-word-games/sound-match/kn/\340\262\252\340\262\225\340\263\215\340\262\267\340\262\277.wav" new file mode 100644 index 00000000..b3742b3d Binary files /dev/null and "b/public/audio/audio-preview/combined-word-games/sound-match/kn/\340\262\252\340\262\225\340\263\215\340\262\267\340\262\277.wav" differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/mr/bread.wav b/public/audio/audio-preview/combined-word-games/sound-match/mr/bread.wav new file mode 100644 index 00000000..ad8827e9 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/mr/bread.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/mr/narration1.wav b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration1.wav new file mode 100644 index 00000000..4b5c74e8 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/mr/narration2.wav b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration2.wav new file mode 100644 index 00000000..527a1c55 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/mr/narration3.wav b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration3.wav new file mode 100644 index 00000000..fb98e8e5 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/mr/narration4.wav b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration4.wav new file mode 100644 index 00000000..267e00cb Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/mr/narration4.wav differ diff --git "a/public/audio/audio-preview/combined-word-games/sound-match/mr/\340\244\270\340\244\253\340\244\260\340\244\232\340\244\202\340\244\246.wav" "b/public/audio/audio-preview/combined-word-games/sound-match/mr/\340\244\270\340\244\253\340\244\260\340\244\232\340\244\202\340\244\246.wav" new file mode 100644 index 00000000..daebc41f Binary files /dev/null and "b/public/audio/audio-preview/combined-word-games/sound-match/mr/\340\244\270\340\244\253\340\244\260\340\244\232\340\244\202\340\244\246.wav" differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/te/bread.wav b/public/audio/audio-preview/combined-word-games/sound-match/te/bread.wav new file mode 100644 index 00000000..ad72011c Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/te/bread.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/te/narration1.wav b/public/audio/audio-preview/combined-word-games/sound-match/te/narration1.wav new file mode 100644 index 00000000..0a4bf084 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/te/narration2.wav b/public/audio/audio-preview/combined-word-games/sound-match/te/narration2.wav new file mode 100644 index 00000000..10ed6d53 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/te/narration3.wav b/public/audio/audio-preview/combined-word-games/sound-match/te/narration3.wav new file mode 100644 index 00000000..f07456cb Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/sound-match/te/narration4.wav b/public/audio/audio-preview/combined-word-games/sound-match/te/narration4.wav new file mode 100644 index 00000000..3394dbd4 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/sound-match/te/narration4.wav differ diff --git "a/public/audio/audio-preview/combined-word-games/sound-match/te/\340\260\252\340\260\225\340\261\215\340\260\267\340\260\277.wav" "b/public/audio/audio-preview/combined-word-games/sound-match/te/\340\260\252\340\260\225\340\261\215\340\260\267\340\260\277.wav" new file mode 100644 index 00000000..7f390243 Binary files /dev/null and "b/public/audio/audio-preview/combined-word-games/sound-match/te/\340\260\252\340\260\225\340\261\215\340\260\267\340\260\277.wav" differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/en/narration1.wav b/public/audio/audio-preview/combined-word-games/word-detective/en/narration1.wav new file mode 100644 index 00000000..b170877e Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/en/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/en/narration2.wav b/public/audio/audio-preview/combined-word-games/word-detective/en/narration2.wav new file mode 100644 index 00000000..25a83518 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/en/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/en/narration3.wav b/public/audio/audio-preview/combined-word-games/word-detective/en/narration3.wav new file mode 100644 index 00000000..880d481f Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/en/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/en/narration4.wav b/public/audio/audio-preview/combined-word-games/word-detective/en/narration4.wav new file mode 100644 index 00000000..7ee81209 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/en/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/hi/narration1.wav b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration1.wav new file mode 100644 index 00000000..bb66f576 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/hi/narration2.wav b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration2.wav new file mode 100644 index 00000000..431c03bf Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/hi/narration3.wav b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration3.wav new file mode 100644 index 00000000..f193dc8d Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/hi/narration4.wav b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration4.wav new file mode 100644 index 00000000..1875021d Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/hi/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/kn/narration1.wav b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration1.wav new file mode 100644 index 00000000..a122f0ff Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/kn/narration2.wav b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration2.wav new file mode 100644 index 00000000..6777f71e Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/kn/narration3.wav b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration3.wav new file mode 100644 index 00000000..27814d17 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/kn/narration4.wav b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration4.wav new file mode 100644 index 00000000..3bc21a7d Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/kn/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/mr/narration1.wav b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration1.wav new file mode 100644 index 00000000..f87305ec Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/mr/narration2.wav b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration2.wav new file mode 100644 index 00000000..2db24a5f Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/mr/narration3.wav b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration3.wav new file mode 100644 index 00000000..71c92d54 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/mr/narration4.wav b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration4.wav new file mode 100644 index 00000000..d20921f9 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/mr/narration4.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/te/narration1.wav b/public/audio/audio-preview/combined-word-games/word-detective/te/narration1.wav new file mode 100644 index 00000000..d11ce39b Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/te/narration1.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/te/narration2.wav b/public/audio/audio-preview/combined-word-games/word-detective/te/narration2.wav new file mode 100644 index 00000000..dc7c9602 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/te/narration2.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/te/narration3.wav b/public/audio/audio-preview/combined-word-games/word-detective/te/narration3.wav new file mode 100644 index 00000000..e65c7ee2 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/te/narration3.wav differ diff --git a/public/audio/audio-preview/combined-word-games/word-detective/te/narration4.wav b/public/audio/audio-preview/combined-word-games/word-detective/te/narration4.wav new file mode 100644 index 00000000..3fd67764 Binary files /dev/null and b/public/audio/audio-preview/combined-word-games/word-detective/te/narration4.wav differ diff --git a/public/audio/audio-preview/failure message/en/failure.wav b/public/audio/audio-preview/failure message/en/failure.wav new file mode 100644 index 00000000..2b888fab Binary files /dev/null and b/public/audio/audio-preview/failure message/en/failure.wav differ diff --git a/public/audio/audio-preview/failure message/hi/failure.wav b/public/audio/audio-preview/failure message/hi/failure.wav new file mode 100644 index 00000000..2f7f9294 Binary files /dev/null and b/public/audio/audio-preview/failure message/hi/failure.wav differ diff --git a/public/audio/audio-preview/failure message/kn/failure.wav b/public/audio/audio-preview/failure message/kn/failure.wav new file mode 100644 index 00000000..cca21101 Binary files /dev/null and b/public/audio/audio-preview/failure message/kn/failure.wav differ diff --git a/public/audio/audio-preview/failure message/mr/failure.wav b/public/audio/audio-preview/failure message/mr/failure.wav new file mode 100644 index 00000000..e4f73eac Binary files /dev/null and b/public/audio/audio-preview/failure message/mr/failure.wav differ diff --git a/public/audio/audio-preview/failure message/te/failure.wav b/public/audio/audio-preview/failure message/te/failure.wav new file mode 100644 index 00000000..a8b1a3bb Binary files /dev/null and b/public/audio/audio-preview/failure message/te/failure.wav differ diff --git a/public/audio/audio-preview/letter-hunt/en/narration1.wav b/public/audio/audio-preview/letter-hunt/en/narration1.wav new file mode 100644 index 00000000..2d8c86d5 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/en/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/en/narration2.wav b/public/audio/audio-preview/letter-hunt/en/narration2.wav new file mode 100644 index 00000000..757eee57 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/en/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/en/narration3.wav b/public/audio/audio-preview/letter-hunt/en/narration3.wav new file mode 100644 index 00000000..f47b51b3 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/en/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/en/narration4.wav b/public/audio/audio-preview/letter-hunt/en/narration4.wav new file mode 100644 index 00000000..f7d67b24 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/en/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/gu/narration1.wav b/public/audio/audio-preview/letter-hunt/gu/narration1.wav new file mode 100644 index 00000000..7bbd35da Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/gu/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/gu/narration2.wav b/public/audio/audio-preview/letter-hunt/gu/narration2.wav new file mode 100644 index 00000000..f53905da Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/gu/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/gu/narration3.wav b/public/audio/audio-preview/letter-hunt/gu/narration3.wav new file mode 100644 index 00000000..d9a43a8a Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/gu/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/gu/narration4.wav b/public/audio/audio-preview/letter-hunt/gu/narration4.wav new file mode 100644 index 00000000..4c214b51 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/gu/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/hi/narration1.wav b/public/audio/audio-preview/letter-hunt/hi/narration1.wav new file mode 100644 index 00000000..b210bc0c Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/hi/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/hi/narration2.wav b/public/audio/audio-preview/letter-hunt/hi/narration2.wav new file mode 100644 index 00000000..733b64e1 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/hi/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/hi/narration3.wav b/public/audio/audio-preview/letter-hunt/hi/narration3.wav new file mode 100644 index 00000000..b4f3131b Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/hi/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/hi/narration4.wav b/public/audio/audio-preview/letter-hunt/hi/narration4.wav new file mode 100644 index 00000000..d503ca37 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/hi/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/kn/narration1.wav b/public/audio/audio-preview/letter-hunt/kn/narration1.wav new file mode 100644 index 00000000..aece55ac Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/kn/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/kn/narration2.wav b/public/audio/audio-preview/letter-hunt/kn/narration2.wav new file mode 100644 index 00000000..7b8784bf Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/kn/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/kn/narration3.wav b/public/audio/audio-preview/letter-hunt/kn/narration3.wav new file mode 100644 index 00000000..3bf391b0 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/kn/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/kn/narration4.wav b/public/audio/audio-preview/letter-hunt/kn/narration4.wav new file mode 100644 index 00000000..ea813718 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/kn/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/mr/narration1.wav b/public/audio/audio-preview/letter-hunt/mr/narration1.wav new file mode 100644 index 00000000..16fa2a77 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/mr/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/mr/narration2.wav b/public/audio/audio-preview/letter-hunt/mr/narration2.wav new file mode 100644 index 00000000..ae022712 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/mr/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/mr/narration3.wav b/public/audio/audio-preview/letter-hunt/mr/narration3.wav new file mode 100644 index 00000000..7858a2fb Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/mr/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/mr/narration4.wav b/public/audio/audio-preview/letter-hunt/mr/narration4.wav new file mode 100644 index 00000000..a7096461 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/mr/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/or/narration1.wav b/public/audio/audio-preview/letter-hunt/or/narration1.wav new file mode 100644 index 00000000..0f331a34 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/or/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/or/narration2.wav b/public/audio/audio-preview/letter-hunt/or/narration2.wav new file mode 100644 index 00000000..e546557f Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/or/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/or/narration3.wav b/public/audio/audio-preview/letter-hunt/or/narration3.wav new file mode 100644 index 00000000..0fcac434 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/or/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/or/narration4.wav b/public/audio/audio-preview/letter-hunt/or/narration4.wav new file mode 100644 index 00000000..acbe8448 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/or/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/ta/narration1.wav b/public/audio/audio-preview/letter-hunt/ta/narration1.wav new file mode 100644 index 00000000..2a097c6f Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/ta/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/ta/narration2.wav b/public/audio/audio-preview/letter-hunt/ta/narration2.wav new file mode 100644 index 00000000..1c56dae2 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/ta/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/ta/narration3.wav b/public/audio/audio-preview/letter-hunt/ta/narration3.wav new file mode 100644 index 00000000..bd694c81 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/ta/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/ta/narration4.wav b/public/audio/audio-preview/letter-hunt/ta/narration4.wav new file mode 100644 index 00000000..110c92b0 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/ta/narration4.wav differ diff --git a/public/audio/audio-preview/letter-hunt/te/narration1.wav b/public/audio/audio-preview/letter-hunt/te/narration1.wav new file mode 100644 index 00000000..426ff8a5 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/te/narration1.wav differ diff --git a/public/audio/audio-preview/letter-hunt/te/narration2.wav b/public/audio/audio-preview/letter-hunt/te/narration2.wav new file mode 100644 index 00000000..fa5b68c5 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/te/narration2.wav differ diff --git a/public/audio/audio-preview/letter-hunt/te/narration3.wav b/public/audio/audio-preview/letter-hunt/te/narration3.wav new file mode 100644 index 00000000..48357ccd Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/te/narration3.wav differ diff --git a/public/audio/audio-preview/letter-hunt/te/narration4.wav b/public/audio/audio-preview/letter-hunt/te/narration4.wav new file mode 100644 index 00000000..66c8b8a6 Binary files /dev/null and b/public/audio/audio-preview/letter-hunt/te/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/en/narration1.wav b/public/audio/audio-preview/sentence-recording/en/narration1.wav new file mode 100644 index 00000000..b9554378 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/en/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/en/narration2.wav b/public/audio/audio-preview/sentence-recording/en/narration2.wav new file mode 100644 index 00000000..2df1ca1c Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/en/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/en/narration3.wav b/public/audio/audio-preview/sentence-recording/en/narration3.wav new file mode 100644 index 00000000..da035a25 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/en/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/en/narration4.wav b/public/audio/audio-preview/sentence-recording/en/narration4.wav new file mode 100644 index 00000000..0c8ff4b4 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/en/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/en/narration5.wav b/public/audio/audio-preview/sentence-recording/en/narration5.wav new file mode 100644 index 00000000..f2ccb68a Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/en/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/gu/narration1.wav b/public/audio/audio-preview/sentence-recording/gu/narration1.wav new file mode 100644 index 00000000..888fcb7f Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/gu/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/gu/narration2.wav b/public/audio/audio-preview/sentence-recording/gu/narration2.wav new file mode 100644 index 00000000..7da618c7 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/gu/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/gu/narration3.wav b/public/audio/audio-preview/sentence-recording/gu/narration3.wav new file mode 100644 index 00000000..936320ab Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/gu/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/gu/narration4.wav b/public/audio/audio-preview/sentence-recording/gu/narration4.wav new file mode 100644 index 00000000..0a459d77 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/gu/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/gu/narration5.wav b/public/audio/audio-preview/sentence-recording/gu/narration5.wav new file mode 100644 index 00000000..da85efd3 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/gu/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/hi/narration1.wav b/public/audio/audio-preview/sentence-recording/hi/narration1.wav new file mode 100644 index 00000000..db5d6c9c Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/hi/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/hi/narration2.wav b/public/audio/audio-preview/sentence-recording/hi/narration2.wav new file mode 100644 index 00000000..36ae5859 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/hi/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/hi/narration3.wav b/public/audio/audio-preview/sentence-recording/hi/narration3.wav new file mode 100644 index 00000000..56d13a32 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/hi/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/hi/narration4.wav b/public/audio/audio-preview/sentence-recording/hi/narration4.wav new file mode 100644 index 00000000..61b2d254 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/hi/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/hi/narration5.wav b/public/audio/audio-preview/sentence-recording/hi/narration5.wav new file mode 100644 index 00000000..280144bb Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/hi/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/kn/narration1.wav b/public/audio/audio-preview/sentence-recording/kn/narration1.wav new file mode 100644 index 00000000..75e74a90 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/kn/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/kn/narration2.wav b/public/audio/audio-preview/sentence-recording/kn/narration2.wav new file mode 100644 index 00000000..17c2d221 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/kn/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/kn/narration3.wav b/public/audio/audio-preview/sentence-recording/kn/narration3.wav new file mode 100644 index 00000000..6343bd69 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/kn/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/kn/narration4.wav b/public/audio/audio-preview/sentence-recording/kn/narration4.wav new file mode 100644 index 00000000..d924aa62 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/kn/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/kn/narration5.wav b/public/audio/audio-preview/sentence-recording/kn/narration5.wav new file mode 100644 index 00000000..c7b838f2 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/kn/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/mr/narration1.wav b/public/audio/audio-preview/sentence-recording/mr/narration1.wav new file mode 100644 index 00000000..0975ff7e Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/mr/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/mr/narration2.wav b/public/audio/audio-preview/sentence-recording/mr/narration2.wav new file mode 100644 index 00000000..ae047364 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/mr/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/mr/narration3.wav b/public/audio/audio-preview/sentence-recording/mr/narration3.wav new file mode 100644 index 00000000..7562d184 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/mr/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/mr/narration4.wav b/public/audio/audio-preview/sentence-recording/mr/narration4.wav new file mode 100644 index 00000000..04fd623d Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/mr/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/mr/narration5.wav b/public/audio/audio-preview/sentence-recording/mr/narration5.wav new file mode 100644 index 00000000..cd327f36 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/mr/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/or/narration1.wav b/public/audio/audio-preview/sentence-recording/or/narration1.wav new file mode 100644 index 00000000..d861d985 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/or/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/or/narration2.wav b/public/audio/audio-preview/sentence-recording/or/narration2.wav new file mode 100644 index 00000000..d75df2e7 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/or/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/or/narration3.wav b/public/audio/audio-preview/sentence-recording/or/narration3.wav new file mode 100644 index 00000000..d603f750 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/or/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/or/narration4.wav b/public/audio/audio-preview/sentence-recording/or/narration4.wav new file mode 100644 index 00000000..165932da Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/or/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/or/narration5.wav b/public/audio/audio-preview/sentence-recording/or/narration5.wav new file mode 100644 index 00000000..59dad6ec Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/or/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/ta/narration1.wav b/public/audio/audio-preview/sentence-recording/ta/narration1.wav new file mode 100644 index 00000000..cc576038 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/ta/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/ta/narration2.wav b/public/audio/audio-preview/sentence-recording/ta/narration2.wav new file mode 100644 index 00000000..80b3ac5b Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/ta/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/ta/narration3.wav b/public/audio/audio-preview/sentence-recording/ta/narration3.wav new file mode 100644 index 00000000..f09620cb Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/ta/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/ta/narration4.wav b/public/audio/audio-preview/sentence-recording/ta/narration4.wav new file mode 100644 index 00000000..aa0f781d Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/ta/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/ta/narration5.wav b/public/audio/audio-preview/sentence-recording/ta/narration5.wav new file mode 100644 index 00000000..beb3c1e8 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/ta/narration5.wav differ diff --git a/public/audio/audio-preview/sentence-recording/te/narration1.wav b/public/audio/audio-preview/sentence-recording/te/narration1.wav new file mode 100644 index 00000000..eb795064 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/te/narration1.wav differ diff --git a/public/audio/audio-preview/sentence-recording/te/narration2.wav b/public/audio/audio-preview/sentence-recording/te/narration2.wav new file mode 100644 index 00000000..e5f50bff Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/te/narration2.wav differ diff --git a/public/audio/audio-preview/sentence-recording/te/narration3.wav b/public/audio/audio-preview/sentence-recording/te/narration3.wav new file mode 100644 index 00000000..aa2fbd60 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/te/narration3.wav differ diff --git a/public/audio/audio-preview/sentence-recording/te/narration4.wav b/public/audio/audio-preview/sentence-recording/te/narration4.wav new file mode 100644 index 00000000..b7070781 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/te/narration4.wav differ diff --git a/public/audio/audio-preview/sentence-recording/te/narration5.wav b/public/audio/audio-preview/sentence-recording/te/narration5.wav new file mode 100644 index 00000000..e8d798f8 Binary files /dev/null and b/public/audio/audio-preview/sentence-recording/te/narration5.wav differ diff --git a/public/audio/audio-preview/success message/en/success.wav b/public/audio/audio-preview/success message/en/success.wav new file mode 100644 index 00000000..ca83414b Binary files /dev/null and b/public/audio/audio-preview/success message/en/success.wav differ diff --git a/public/audio/audio-preview/success message/hi/success.wav b/public/audio/audio-preview/success message/hi/success.wav new file mode 100644 index 00000000..17e4b130 Binary files /dev/null and b/public/audio/audio-preview/success message/hi/success.wav differ diff --git a/public/audio/audio-preview/success message/kn/success.wav b/public/audio/audio-preview/success message/kn/success.wav new file mode 100644 index 00000000..831ff967 Binary files /dev/null and b/public/audio/audio-preview/success message/kn/success.wav differ diff --git a/public/audio/audio-preview/success message/mr/success.wav b/public/audio/audio-preview/success message/mr/success.wav new file mode 100644 index 00000000..849cbb01 Binary files /dev/null and b/public/audio/audio-preview/success message/mr/success.wav differ diff --git a/public/audio/audio-preview/success message/te/success.wav b/public/audio/audio-preview/success message/te/success.wav new file mode 100644 index 00000000..48a7bff7 Binary files /dev/null and b/public/audio/audio-preview/success message/te/success.wav differ diff --git a/public/audio/english/letter/A.wav b/public/audio/english/letter/A.wav new file mode 100644 index 00000000..352d27f7 Binary files /dev/null and b/public/audio/english/letter/A.wav differ diff --git a/public/audio/english/letter/ACT.wav b/public/audio/english/letter/ACT.wav new file mode 100644 index 00000000..8510b7af Binary files /dev/null and b/public/audio/english/letter/ACT.wav differ diff --git a/public/audio/english/letter/AGE.wav b/public/audio/english/letter/AGE.wav new file mode 100644 index 00000000..cfbebba0 Binary files /dev/null and b/public/audio/english/letter/AGE.wav differ diff --git a/public/audio/english/letter/AIR.wav b/public/audio/english/letter/AIR.wav new file mode 100644 index 00000000..89203b39 Binary files /dev/null and b/public/audio/english/letter/AIR.wav differ diff --git a/public/audio/english/letter/ALL.wav b/public/audio/english/letter/ALL.wav new file mode 100644 index 00000000..287f1b18 Binary files /dev/null and b/public/audio/english/letter/ALL.wav differ diff --git a/public/audio/english/letter/AM.wav b/public/audio/english/letter/AM.wav new file mode 100644 index 00000000..08ef181a Binary files /dev/null and b/public/audio/english/letter/AM.wav differ diff --git a/public/audio/english/letter/AN.wav b/public/audio/english/letter/AN.wav new file mode 100644 index 00000000..eae6a4eb Binary files /dev/null and b/public/audio/english/letter/AN.wav differ diff --git a/public/audio/english/letter/AND.wav b/public/audio/english/letter/AND.wav new file mode 100644 index 00000000..43bfc729 Binary files /dev/null and b/public/audio/english/letter/AND.wav differ diff --git a/public/audio/english/letter/ARE.wav b/public/audio/english/letter/ARE.wav new file mode 100644 index 00000000..0368f260 Binary files /dev/null and b/public/audio/english/letter/ARE.wav differ diff --git a/public/audio/english/letter/ARM.wav b/public/audio/english/letter/ARM.wav new file mode 100644 index 00000000..9c946efe Binary files /dev/null and b/public/audio/english/letter/ARM.wav differ diff --git a/public/audio/english/letter/ART.wav b/public/audio/english/letter/ART.wav new file mode 100644 index 00000000..9f3e5914 Binary files /dev/null and b/public/audio/english/letter/ART.wav differ diff --git a/public/audio/english/letter/AS.wav b/public/audio/english/letter/AS.wav new file mode 100644 index 00000000..fda964cf Binary files /dev/null and b/public/audio/english/letter/AS.wav differ diff --git a/public/audio/english/letter/ASK.wav b/public/audio/english/letter/ASK.wav new file mode 100644 index 00000000..4afab740 Binary files /dev/null and b/public/audio/english/letter/ASK.wav differ diff --git a/public/audio/english/letter/AT.wav b/public/audio/english/letter/AT.wav new file mode 100644 index 00000000..62e5b5b6 Binary files /dev/null and b/public/audio/english/letter/AT.wav differ diff --git a/public/audio/english/letter/ATE.wav b/public/audio/english/letter/ATE.wav new file mode 100644 index 00000000..ebc8ac46 Binary files /dev/null and b/public/audio/english/letter/ATE.wav differ diff --git a/public/audio/english/letter/B.wav b/public/audio/english/letter/B.wav new file mode 100644 index 00000000..9a1a67be Binary files /dev/null and b/public/audio/english/letter/B.wav differ diff --git a/public/audio/english/letter/BE.wav b/public/audio/english/letter/BE.wav new file mode 100644 index 00000000..db8545ba Binary files /dev/null and b/public/audio/english/letter/BE.wav differ diff --git a/public/audio/english/letter/BED.wav b/public/audio/english/letter/BED.wav new file mode 100644 index 00000000..6c593ab5 Binary files /dev/null and b/public/audio/english/letter/BED.wav differ diff --git a/public/audio/english/letter/BIT.wav b/public/audio/english/letter/BIT.wav new file mode 100644 index 00000000..80e7e976 Binary files /dev/null and b/public/audio/english/letter/BIT.wav differ diff --git a/public/audio/english/letter/C.wav b/public/audio/english/letter/C.wav new file mode 100644 index 00000000..b10f4e17 Binary files /dev/null and b/public/audio/english/letter/C.wav differ diff --git a/public/audio/english/letter/CAR.wav b/public/audio/english/letter/CAR.wav new file mode 100644 index 00000000..32a3c299 Binary files /dev/null and b/public/audio/english/letter/CAR.wav differ diff --git a/public/audio/english/letter/D.wav b/public/audio/english/letter/D.wav new file mode 100644 index 00000000..9cf46e23 Binary files /dev/null and b/public/audio/english/letter/D.wav differ diff --git a/public/audio/english/letter/DO.wav b/public/audio/english/letter/DO.wav new file mode 100644 index 00000000..18666696 Binary files /dev/null and b/public/audio/english/letter/DO.wav differ diff --git a/public/audio/english/letter/E.wav b/public/audio/english/letter/E.wav new file mode 100644 index 00000000..c53ef106 Binary files /dev/null and b/public/audio/english/letter/E.wav differ diff --git a/public/audio/english/letter/EAR.wav b/public/audio/english/letter/EAR.wav new file mode 100644 index 00000000..2d5bae5a Binary files /dev/null and b/public/audio/english/letter/EAR.wav differ diff --git a/public/audio/english/letter/F.wav b/public/audio/english/letter/F.wav new file mode 100644 index 00000000..8ec88249 Binary files /dev/null and b/public/audio/english/letter/F.wav differ diff --git a/public/audio/english/letter/FIT.wav b/public/audio/english/letter/FIT.wav new file mode 100644 index 00000000..fdaab4ef Binary files /dev/null and b/public/audio/english/letter/FIT.wav differ diff --git a/public/audio/english/letter/FLY.wav b/public/audio/english/letter/FLY.wav new file mode 100644 index 00000000..e29bcbeb Binary files /dev/null and b/public/audio/english/letter/FLY.wav differ diff --git a/public/audio/english/letter/FOR.wav b/public/audio/english/letter/FOR.wav new file mode 100644 index 00000000..8cd66006 Binary files /dev/null and b/public/audio/english/letter/FOR.wav differ diff --git a/public/audio/english/letter/FUN.wav b/public/audio/english/letter/FUN.wav new file mode 100644 index 00000000..52ef1a92 Binary files /dev/null and b/public/audio/english/letter/FUN.wav differ diff --git a/public/audio/english/letter/G.wav b/public/audio/english/letter/G.wav new file mode 100644 index 00000000..af5aaaa5 Binary files /dev/null and b/public/audio/english/letter/G.wav differ diff --git a/public/audio/english/letter/GO.wav b/public/audio/english/letter/GO.wav new file mode 100644 index 00000000..61b867c4 Binary files /dev/null and b/public/audio/english/letter/GO.wav differ diff --git a/public/audio/english/letter/H.wav b/public/audio/english/letter/H.wav new file mode 100644 index 00000000..fadae7c0 Binary files /dev/null and b/public/audio/english/letter/H.wav differ diff --git a/public/audio/english/letter/HAT.wav b/public/audio/english/letter/HAT.wav new file mode 100644 index 00000000..f1b02478 Binary files /dev/null and b/public/audio/english/letter/HAT.wav differ diff --git a/public/audio/english/letter/HE.wav b/public/audio/english/letter/HE.wav new file mode 100644 index 00000000..63f4ebf7 Binary files /dev/null and b/public/audio/english/letter/HE.wav differ diff --git a/public/audio/english/letter/HER.wav b/public/audio/english/letter/HER.wav new file mode 100644 index 00000000..7d4a8780 Binary files /dev/null and b/public/audio/english/letter/HER.wav differ diff --git a/public/audio/english/letter/HI.wav b/public/audio/english/letter/HI.wav new file mode 100644 index 00000000..68fed648 Binary files /dev/null and b/public/audio/english/letter/HI.wav differ diff --git a/public/audio/english/letter/HIM.wav b/public/audio/english/letter/HIM.wav new file mode 100644 index 00000000..b81eedeb Binary files /dev/null and b/public/audio/english/letter/HIM.wav differ diff --git a/public/audio/english/letter/HIS.wav b/public/audio/english/letter/HIS.wav new file mode 100644 index 00000000..7ce3eac3 Binary files /dev/null and b/public/audio/english/letter/HIS.wav differ diff --git a/public/audio/english/letter/I.wav b/public/audio/english/letter/I.wav new file mode 100644 index 00000000..5a508157 Binary files /dev/null and b/public/audio/english/letter/I.wav differ diff --git a/public/audio/english/letter/ICE.wav b/public/audio/english/letter/ICE.wav new file mode 100644 index 00000000..6201510b Binary files /dev/null and b/public/audio/english/letter/ICE.wav differ diff --git a/public/audio/english/letter/IN.wav b/public/audio/english/letter/IN.wav new file mode 100644 index 00000000..5b4d296e Binary files /dev/null and b/public/audio/english/letter/IN.wav differ diff --git a/public/audio/english/letter/ING.wav b/public/audio/english/letter/ING.wav new file mode 100644 index 00000000..a7ed9ea7 Binary files /dev/null and b/public/audio/english/letter/ING.wav differ diff --git a/public/audio/english/letter/INK.wav b/public/audio/english/letter/INK.wav new file mode 100644 index 00000000..f1711ad1 Binary files /dev/null and b/public/audio/english/letter/INK.wav differ diff --git a/public/audio/english/letter/IS.wav b/public/audio/english/letter/IS.wav new file mode 100644 index 00000000..e5818c75 Binary files /dev/null and b/public/audio/english/letter/IS.wav differ diff --git a/public/audio/english/letter/IT.wav b/public/audio/english/letter/IT.wav new file mode 100644 index 00000000..ea76a40b Binary files /dev/null and b/public/audio/english/letter/IT.wav differ diff --git a/public/audio/english/letter/J.wav b/public/audio/english/letter/J.wav new file mode 100644 index 00000000..a92817a4 Binary files /dev/null and b/public/audio/english/letter/J.wav differ diff --git a/public/audio/english/letter/K.wav b/public/audio/english/letter/K.wav new file mode 100644 index 00000000..dcefdd96 Binary files /dev/null and b/public/audio/english/letter/K.wav differ diff --git a/public/audio/english/letter/KEY.wav b/public/audio/english/letter/KEY.wav new file mode 100644 index 00000000..6681dab5 Binary files /dev/null and b/public/audio/english/letter/KEY.wav differ diff --git a/public/audio/english/letter/L.wav b/public/audio/english/letter/L.wav new file mode 100644 index 00000000..6a59c188 Binary files /dev/null and b/public/audio/english/letter/L.wav differ diff --git a/public/audio/english/letter/M.wav b/public/audio/english/letter/M.wav new file mode 100644 index 00000000..64a53827 Binary files /dev/null and b/public/audio/english/letter/M.wav differ diff --git a/public/audio/english/letter/ME.wav b/public/audio/english/letter/ME.wav new file mode 100644 index 00000000..7837f5fd Binary files /dev/null and b/public/audio/english/letter/ME.wav differ diff --git a/public/audio/english/letter/MEN.wav b/public/audio/english/letter/MEN.wav new file mode 100644 index 00000000..0111af6d Binary files /dev/null and b/public/audio/english/letter/MEN.wav differ diff --git a/public/audio/english/letter/MY.wav b/public/audio/english/letter/MY.wav new file mode 100644 index 00000000..da74d7f2 Binary files /dev/null and b/public/audio/english/letter/MY.wav differ diff --git a/public/audio/english/letter/N.wav b/public/audio/english/letter/N.wav new file mode 100644 index 00000000..adc1cd10 Binary files /dev/null and b/public/audio/english/letter/N.wav differ diff --git a/public/audio/english/letter/NO.wav b/public/audio/english/letter/NO.wav new file mode 100644 index 00000000..a64e97cc Binary files /dev/null and b/public/audio/english/letter/NO.wav differ diff --git a/public/audio/english/letter/NOT.wav b/public/audio/english/letter/NOT.wav new file mode 100644 index 00000000..bae32ec5 Binary files /dev/null and b/public/audio/english/letter/NOT.wav differ diff --git a/public/audio/english/letter/O.wav b/public/audio/english/letter/O.wav new file mode 100644 index 00000000..6b4469fd Binary files /dev/null and b/public/audio/english/letter/O.wav differ diff --git a/public/audio/english/letter/OF.wav b/public/audio/english/letter/OF.wav new file mode 100644 index 00000000..a7d001ae Binary files /dev/null and b/public/audio/english/letter/OF.wav differ diff --git a/public/audio/english/letter/ON.wav b/public/audio/english/letter/ON.wav new file mode 100644 index 00000000..3b9316e0 Binary files /dev/null and b/public/audio/english/letter/ON.wav differ diff --git a/public/audio/english/letter/ONE.wav b/public/audio/english/letter/ONE.wav new file mode 100644 index 00000000..3a179df4 Binary files /dev/null and b/public/audio/english/letter/ONE.wav differ diff --git a/public/audio/english/letter/OR.wav b/public/audio/english/letter/OR.wav new file mode 100644 index 00000000..4e7179e9 Binary files /dev/null and b/public/audio/english/letter/OR.wav differ diff --git a/public/audio/english/letter/OUR.wav b/public/audio/english/letter/OUR.wav new file mode 100644 index 00000000..ae91b686 Binary files /dev/null and b/public/audio/english/letter/OUR.wav differ diff --git a/public/audio/english/letter/OX.wav b/public/audio/english/letter/OX.wav new file mode 100644 index 00000000..1cf8ef80 Binary files /dev/null and b/public/audio/english/letter/OX.wav differ diff --git a/public/audio/english/letter/P.wav b/public/audio/english/letter/P.wav new file mode 100644 index 00000000..e37008ec Binary files /dev/null and b/public/audio/english/letter/P.wav differ diff --git a/public/audio/english/letter/Q.wav b/public/audio/english/letter/Q.wav new file mode 100644 index 00000000..b7a4de63 Binary files /dev/null and b/public/audio/english/letter/Q.wav differ diff --git a/public/audio/english/letter/R.wav b/public/audio/english/letter/R.wav new file mode 100644 index 00000000..3b25cc18 Binary files /dev/null and b/public/audio/english/letter/R.wav differ diff --git a/public/audio/english/letter/RAT.wav b/public/audio/english/letter/RAT.wav new file mode 100644 index 00000000..242dcba1 Binary files /dev/null and b/public/audio/english/letter/RAT.wav differ diff --git a/public/audio/english/letter/S.wav b/public/audio/english/letter/S.wav new file mode 100644 index 00000000..20ec98ea Binary files /dev/null and b/public/audio/english/letter/S.wav differ diff --git a/public/audio/english/letter/SHE.wav b/public/audio/english/letter/SHE.wav new file mode 100644 index 00000000..46f69a1b Binary files /dev/null and b/public/audio/english/letter/SHE.wav differ diff --git a/public/audio/english/letter/SO.wav b/public/audio/english/letter/SO.wav new file mode 100644 index 00000000..89885fe3 Binary files /dev/null and b/public/audio/english/letter/SO.wav differ diff --git a/public/audio/english/letter/T.wav b/public/audio/english/letter/T.wav new file mode 100644 index 00000000..1df25c61 Binary files /dev/null and b/public/audio/english/letter/T.wav differ diff --git a/public/audio/english/letter/TER.wav b/public/audio/english/letter/TER.wav new file mode 100644 index 00000000..26798962 Binary files /dev/null and b/public/audio/english/letter/TER.wav differ diff --git a/public/audio/english/letter/THE.wav b/public/audio/english/letter/THE.wav new file mode 100644 index 00000000..ee39651a Binary files /dev/null and b/public/audio/english/letter/THE.wav differ diff --git a/public/audio/english/letter/TO.wav b/public/audio/english/letter/TO.wav new file mode 100644 index 00000000..397f4fdc Binary files /dev/null and b/public/audio/english/letter/TO.wav differ diff --git a/public/audio/english/letter/U.wav b/public/audio/english/letter/U.wav new file mode 100644 index 00000000..facd8755 Binary files /dev/null and b/public/audio/english/letter/U.wav differ diff --git a/public/audio/english/letter/UP.wav b/public/audio/english/letter/UP.wav new file mode 100644 index 00000000..b61eeeae Binary files /dev/null and b/public/audio/english/letter/UP.wav differ diff --git a/public/audio/english/letter/US.wav b/public/audio/english/letter/US.wav new file mode 100644 index 00000000..3581d3cd Binary files /dev/null and b/public/audio/english/letter/US.wav differ diff --git a/public/audio/english/letter/V.wav b/public/audio/english/letter/V.wav new file mode 100644 index 00000000..6ed8f3ad Binary files /dev/null and b/public/audio/english/letter/V.wav differ diff --git a/public/audio/english/letter/W.wav b/public/audio/english/letter/W.wav new file mode 100644 index 00000000..1ff5fbf0 Binary files /dev/null and b/public/audio/english/letter/W.wav differ diff --git a/public/audio/english/letter/WAS.wav b/public/audio/english/letter/WAS.wav new file mode 100644 index 00000000..ddeb3c9d Binary files /dev/null and b/public/audio/english/letter/WAS.wav differ diff --git a/public/audio/english/letter/WIT.wav b/public/audio/english/letter/WIT.wav new file mode 100644 index 00000000..b85686f9 Binary files /dev/null and b/public/audio/english/letter/WIT.wav differ diff --git a/public/audio/english/letter/X.wav b/public/audio/english/letter/X.wav new file mode 100644 index 00000000..78c8d0e2 Binary files /dev/null and b/public/audio/english/letter/X.wav differ diff --git a/public/audio/english/letter/Y.wav b/public/audio/english/letter/Y.wav new file mode 100644 index 00000000..2860e3bb Binary files /dev/null and b/public/audio/english/letter/Y.wav differ diff --git a/public/audio/english/letter/Z.wav b/public/audio/english/letter/Z.wav new file mode 100644 index 00000000..3b6daa88 Binary files /dev/null and b/public/audio/english/letter/Z.wav differ diff --git "a/public/audio/hindi/letter/\340\244\205.wav" "b/public/audio/hindi/letter/\340\244\205.wav" new file mode 100644 index 00000000..3927c46d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\205.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\205\340\244\202.wav" "b/public/audio/hindi/letter/\340\244\205\340\244\202.wav" new file mode 100644 index 00000000..1539f9b8 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\205\340\244\202.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\205\340\244\203.wav" "b/public/audio/hindi/letter/\340\244\205\340\244\203.wav" new file mode 100644 index 00000000..353d304c Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\205\340\244\203.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\206.wav" "b/public/audio/hindi/letter/\340\244\206.wav" new file mode 100644 index 00000000..5e55a9cd Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\206.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\207.wav" "b/public/audio/hindi/letter/\340\244\207.wav" new file mode 100644 index 00000000..e2e123b3 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\207.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\210.wav" "b/public/audio/hindi/letter/\340\244\210.wav" new file mode 100644 index 00000000..03e8c3e6 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\210.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\211.wav" "b/public/audio/hindi/letter/\340\244\211.wav" new file mode 100644 index 00000000..b697fe23 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\211.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\212.wav" "b/public/audio/hindi/letter/\340\244\212.wav" new file mode 100644 index 00000000..96a7bdbc Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\212.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\213.wav" "b/public/audio/hindi/letter/\340\244\213.wav" new file mode 100644 index 00000000..75841310 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\213.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\217.wav" "b/public/audio/hindi/letter/\340\244\217.wav" new file mode 100644 index 00000000..de7c8c0c Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\217.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\220.wav" "b/public/audio/hindi/letter/\340\244\220.wav" new file mode 100644 index 00000000..abbd6e17 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\220.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\223.wav" "b/public/audio/hindi/letter/\340\244\223.wav" new file mode 100644 index 00000000..7d9f16f1 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\223.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\224.wav" "b/public/audio/hindi/letter/\340\244\224.wav" new file mode 100644 index 00000000..6c1520d0 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\224.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225.wav" "b/public/audio/hindi/letter/\340\244\225.wav" new file mode 100644 index 00000000..426b4cd5 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\225\340\244\276.wav" new file mode 100644 index 00000000..61348b47 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\244\277.wav" "b/public/audio/hindi/letter/\340\244\225\340\244\277.wav" new file mode 100644 index 00000000..534cdd43 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\244\277.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\245\203.wav" "b/public/audio/hindi/letter/\340\244\225\340\245\203.wav" new file mode 100644 index 00000000..8e1627d5 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\245\203.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\245\207.wav" "b/public/audio/hindi/letter/\340\244\225\340\245\207.wav" new file mode 100644 index 00000000..28718b33 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\245\207.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\245\210.wav" "b/public/audio/hindi/letter/\340\244\225\340\245\210.wav" new file mode 100644 index 00000000..f32782fa Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\245\210.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\245\214.wav" "b/public/audio/hindi/letter/\340\244\225\340\245\214.wav" new file mode 100644 index 00000000..140bec9e Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\245\214.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\225\340\245\215\340\244\267.wav" "b/public/audio/hindi/letter/\340\244\225\340\245\215\340\244\267.wav" new file mode 100644 index 00000000..879cb642 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\225\340\245\215\340\244\267.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\226.wav" "b/public/audio/hindi/letter/\340\244\226.wav" new file mode 100644 index 00000000..d1c7ba59 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\226.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\227.wav" "b/public/audio/hindi/letter/\340\244\227.wav" new file mode 100644 index 00000000..4dc7d7e2 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\227.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\227\340\245\200.wav" "b/public/audio/hindi/letter/\340\244\227\340\245\200.wav" new file mode 100644 index 00000000..58ee925f Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\227\340\245\200.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\227\340\245\203.wav" "b/public/audio/hindi/letter/\340\244\227\340\245\203.wav" new file mode 100644 index 00000000..5e634d32 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\227\340\245\203.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\230.wav" "b/public/audio/hindi/letter/\340\244\230.wav" new file mode 100644 index 00000000..aa99df8b Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\230.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\231.wav" "b/public/audio/hindi/letter/\340\244\231.wav" new file mode 100644 index 00000000..91d09a39 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\231.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\232.wav" "b/public/audio/hindi/letter/\340\244\232.wav" new file mode 100644 index 00000000..2f101876 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\232.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\233.wav" "b/public/audio/hindi/letter/\340\244\233.wav" new file mode 100644 index 00000000..4ecdd0a2 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\233.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\234.wav" "b/public/audio/hindi/letter/\340\244\234.wav" new file mode 100644 index 00000000..fe6381ee Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\234.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\234\340\245\200.wav" "b/public/audio/hindi/letter/\340\244\234\340\245\200.wav" new file mode 100644 index 00000000..134e17cb Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\234\340\245\200.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\234\340\245\215\340\244\236.wav" "b/public/audio/hindi/letter/\340\244\234\340\245\215\340\244\236.wav" new file mode 100644 index 00000000..b468c0a0 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\234\340\245\215\340\244\236.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\235.wav" "b/public/audio/hindi/letter/\340\244\235.wav" new file mode 100644 index 00000000..3f2bc9c8 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\235.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\236.wav" "b/public/audio/hindi/letter/\340\244\236.wav" new file mode 100644 index 00000000..9a0772f8 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\236.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\237.wav" "b/public/audio/hindi/letter/\340\244\237.wav" new file mode 100644 index 00000000..f3762856 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\237.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\240.wav" "b/public/audio/hindi/letter/\340\244\240.wav" new file mode 100644 index 00000000..83a60c7f Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\240.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\241.wav" "b/public/audio/hindi/letter/\340\244\241.wav" new file mode 100644 index 00000000..1c592770 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\241.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\241\340\244\274\340\244\277.wav" "b/public/audio/hindi/letter/\340\244\241\340\244\274\340\244\277.wav" new file mode 100644 index 00000000..061bf8f4 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\241\340\244\274\340\244\277.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\242.wav" "b/public/audio/hindi/letter/\340\244\242.wav" new file mode 100644 index 00000000..f3ce92dd Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\242.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\243.wav" "b/public/audio/hindi/letter/\340\244\243.wav" new file mode 100644 index 00000000..4a1f4371 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\243.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\244.wav" "b/public/audio/hindi/letter/\340\244\244.wav" new file mode 100644 index 00000000..df8dbae9 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\244.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\244\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\244\340\244\276.wav" new file mode 100644 index 00000000..232de2b1 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\244\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\244\340\245\200.wav" "b/public/audio/hindi/letter/\340\244\244\340\245\200.wav" new file mode 100644 index 00000000..78c920ab Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\244\340\245\200.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\244\340\245\207.wav" "b/public/audio/hindi/letter/\340\244\244\340\245\207.wav" new file mode 100644 index 00000000..a17d8144 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\244\340\245\207.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\244\340\245\215\340\244\260.wav" "b/public/audio/hindi/letter/\340\244\244\340\245\215\340\244\260.wav" new file mode 100644 index 00000000..69732780 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\244\340\245\215\340\244\260.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\245.wav" "b/public/audio/hindi/letter/\340\244\245.wav" new file mode 100644 index 00000000..ee4461f2 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\245.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\245\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\245\340\244\276.wav" new file mode 100644 index 00000000..c52cfb0a Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\245\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\246.wav" "b/public/audio/hindi/letter/\340\244\246.wav" new file mode 100644 index 00000000..ce7a2e27 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\246.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\246\340\244\277.wav" "b/public/audio/hindi/letter/\340\244\246\340\244\277.wav" new file mode 100644 index 00000000..6f4b6282 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\246\340\244\277.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\247.wav" "b/public/audio/hindi/letter/\340\244\247.wav" new file mode 100644 index 00000000..8edf28a9 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\247.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\250.wav" "b/public/audio/hindi/letter/\340\244\250.wav" new file mode 100644 index 00000000..88d2c1c8 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\250.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\250\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\250\340\244\276.wav" new file mode 100644 index 00000000..4829816c Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\250\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\250\340\245\200.wav" "b/public/audio/hindi/letter/\340\244\250\340\245\200.wav" new file mode 100644 index 00000000..5ccbc989 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\250\340\245\200.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\250\340\245\207.wav" "b/public/audio/hindi/letter/\340\244\250\340\245\207.wav" new file mode 100644 index 00000000..f41f79aa Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\250\340\245\207.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\252.wav" "b/public/audio/hindi/letter/\340\244\252.wav" new file mode 100644 index 00000000..cf99111d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\252.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\252\340\244\277.wav" "b/public/audio/hindi/letter/\340\244\252\340\244\277.wav" new file mode 100644 index 00000000..9dcd0fbe Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\252\340\244\277.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\253.wav" "b/public/audio/hindi/letter/\340\244\253.wav" new file mode 100644 index 00000000..770bb555 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\253.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\253\340\245\202.wav" "b/public/audio/hindi/letter/\340\244\253\340\245\202.wav" new file mode 100644 index 00000000..b4eeb171 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\253\340\245\202.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\254.wav" "b/public/audio/hindi/letter/\340\244\254.wav" new file mode 100644 index 00000000..733fb363 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\254.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\254\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\254\340\244\276.wav" new file mode 100644 index 00000000..18a28235 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\254\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\254\340\245\210.wav" "b/public/audio/hindi/letter/\340\244\254\340\245\210.wav" new file mode 100644 index 00000000..50031529 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\254\340\245\210.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\255.wav" "b/public/audio/hindi/letter/\340\244\255.wav" new file mode 100644 index 00000000..8317b26d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\255.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\256.wav" "b/public/audio/hindi/letter/\340\244\256.wav" new file mode 100644 index 00000000..190d0e91 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\256.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\256\340\244\203.wav" "b/public/audio/hindi/letter/\340\244\256\340\244\203.wav" new file mode 100644 index 00000000..716fefbd Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\256\340\244\203.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\256\340\244\277.wav" "b/public/audio/hindi/letter/\340\244\256\340\244\277.wav" new file mode 100644 index 00000000..4035c50a Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\256\340\244\277.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\256\340\245\201.wav" "b/public/audio/hindi/letter/\340\244\256\340\245\201.wav" new file mode 100644 index 00000000..f3642a1e Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\256\340\245\201.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\256\340\245\213.wav" "b/public/audio/hindi/letter/\340\244\256\340\245\213.wav" new file mode 100644 index 00000000..f3f97da5 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\256\340\245\213.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\256\340\245\214.wav" "b/public/audio/hindi/letter/\340\244\256\340\245\214.wav" new file mode 100644 index 00000000..6ff08a04 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\256\340\245\214.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\257.wav" "b/public/audio/hindi/letter/\340\244\257.wav" new file mode 100644 index 00000000..0cbdabd5 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\257.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\257\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\257\340\244\276.wav" new file mode 100644 index 00000000..800e254f Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\257\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\260.wav" "b/public/audio/hindi/letter/\340\244\260.wav" new file mode 100644 index 00000000..94517e1d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\260.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\260\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\260\340\244\276.wav" new file mode 100644 index 00000000..4884b26f Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\260\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\260\340\245\200.wav" "b/public/audio/hindi/letter/\340\244\260\340\245\200.wav" new file mode 100644 index 00000000..232b8e35 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\260\340\245\200.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\260\340\245\201.wav" "b/public/audio/hindi/letter/\340\244\260\340\245\201.wav" new file mode 100644 index 00000000..c1c9dcbe Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\260\340\245\201.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\260\340\245\202.wav" "b/public/audio/hindi/letter/\340\244\260\340\245\202.wav" new file mode 100644 index 00000000..b31d0315 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\260\340\245\202.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\262.wav" "b/public/audio/hindi/letter/\340\244\262.wav" new file mode 100644 index 00000000..2bbafa5d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\262.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\262\340\245\200.wav" "b/public/audio/hindi/letter/\340\244\262\340\245\200.wav" new file mode 100644 index 00000000..4f69db0d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\262\340\245\200.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\265.wav" "b/public/audio/hindi/letter/\340\244\265.wav" new file mode 100644 index 00000000..73e11b56 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\265.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\266.wav" "b/public/audio/hindi/letter/\340\244\266.wav" new file mode 100644 index 00000000..70b8741b Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\266.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\266\340\245\201.wav" "b/public/audio/hindi/letter/\340\244\266\340\245\201.wav" new file mode 100644 index 00000000..46a2232f Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\266\340\245\201.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\266\340\245\213.wav" "b/public/audio/hindi/letter/\340\244\266\340\245\213.wav" new file mode 100644 index 00000000..1a76089c Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\266\340\245\213.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\267.wav" "b/public/audio/hindi/letter/\340\244\267.wav" new file mode 100644 index 00000000..be61da86 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\267.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\270.wav" "b/public/audio/hindi/letter/\340\244\270.wav" new file mode 100644 index 00000000..cb31ad7d Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\270.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\270\340\245\201.wav" "b/public/audio/hindi/letter/\340\244\270\340\245\201.wav" new file mode 100644 index 00000000..2fb7caca Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\270\340\245\201.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\271.wav" "b/public/audio/hindi/letter/\340\244\271.wav" new file mode 100644 index 00000000..00165209 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\271.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\271\340\244\202.wav" "b/public/audio/hindi/letter/\340\244\271\340\244\202.wav" new file mode 100644 index 00000000..bcdb5f1b Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\271\340\244\202.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\271\340\244\276.wav" "b/public/audio/hindi/letter/\340\244\271\340\244\276.wav" new file mode 100644 index 00000000..db498b81 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\271\340\244\276.wav" differ diff --git "a/public/audio/hindi/letter/\340\244\271\340\245\213.wav" "b/public/audio/hindi/letter/\340\244\271\340\245\213.wav" new file mode 100644 index 00000000..faaeb496 Binary files /dev/null and "b/public/audio/hindi/letter/\340\244\271\340\245\213.wav" differ diff --git a/public/audio/kannada/README.md b/public/audio/kannada/README.md new file mode 100644 index 00000000..a42604fa --- /dev/null +++ b/public/audio/kannada/README.md @@ -0,0 +1,74 @@ +# Kannada Audio Files + +This directory contains audio files for Kannada language support in the letter games. + +## Directory Structure + +``` +kannada/ +├── letter/ # Individual letter pronunciation files +│ ├── Level 1/ # Basic vowels: ಅ, ಆ, ಇ, ಈ, ಉ, ಊ, ಋ, ಎ, ಏ +│ ├── Level 2/ # Vowel modifiers and consonants: ಅಂ, ಅಃ, ಕ, ಖ, ಗ, ಘ, ಙ, ಚ, ಛ +│ ├── Level 3/ # Consonants: ತ, ಥ, ದ, ಧ, ನ, ಪ, ಫ, ಬ, ಭ +│ ├── Level 4/ # More consonants: ಹ, ಡ, ಢ, ಣ, ಟ, ಠ, ಡ, ಢ +│ ├── Level 5/ # Advanced consonants: ರ, ಫ, ಛ, etc. +│ ├── Level 6/ # Complex syllables: ಜ್ಞ, ರು, ಸ್ನು, ದು +│ ├── Level 7/ # Syllables with matras: ಳಿ, ಸಿ, ಡಿ, ಲ್ಲಿ, ಗಿ, ರೊ, ಸು, ಳು, ಮಾ +│ ├── Level 8/ # More syllables: ದಿ, ವು, ಡು, ವಾ, ಸು, ತಿ, ಗು, ನಿ, ತು +│ ├── Level 9/ # Advanced syllables: ನೆ, ಕಾ, ಕೆ, ಯಾ, ವಿ, ಲು, ಲಿ, ನಾ, ಕು +│ └── Level 10/ # Expert syllables: ಯಿ, ಹಾ, ರಾ, ತೆ, ದೆ, ಹೇ, ತ್ತು, ಕೊ, ಬಾ +└── README.md # This file +``` + +## Audio File Naming Convention + +- Each letter/syllable should have its own `.wav` file +- File names should match the exact Kannada character +- Example: `ಅ.wav`, `ಆ.wav`, `ಕ.wav`, `ಜ್ಞ.wav` + +## Level-wise Content + +### Level 1 (Basic Vowels) +- ಅ, ಆ, ಇ, ಈ, ಉ, ಊ, ಋ, ಎ, ಏ + +### Level 2 (Vowel Modifiers + Basic Consonants) +- ಅಂ, ಅಃ, ಕ, ಖ, ಗ, ಘ, ಙ, ಚ, ಛ + +### Level 3 (Consonants) +- ತ, ಥ, ದ, ಧ, ನ, ಪ, ಫ, ಬ, ಭ + +### Level 4 (More Consonants) +- ಹ, ಡ, ಢ, ಣ, ಟ, ಠ, ಡ, ಢ + +### Level 5 (Advanced Consonants) +- ರ, ಫ, ಛ, etc. + +### Level 6 (Complex Syllables) +- ಜ್ಞ, ರು, ಸ್ನು, ದು + +### Level 7 (Syllables with Matras) +- ಳಿ, ಸಿ, ಡಿ, ಲ್ಲಿ, ಗಿ, ರೊ, ಸು, ಳು, ಮಾ + +### Level 8 (More Syllables) +- ದಿ, ವು, ಡು, ವಾ, ಸು, ತಿ, ಗು, ನಿ, ತು + +### Level 9 (Advanced Syllables) +- ನೆ, ಕಾ, ಕೆ, ಯಾ, ವಿ, ಲು, ಲಿ, ನಾ, ಕು + +### Level 10 (Expert Syllables) +- ಯಿ, ಹಾ, ರಾ, ತೆ, ದೆ, ಹೇ, ತ್ತು, ಕೊ, ಬಾ + +## Usage + +These audio files are used by the Kannada audio manager to provide pronunciation support for letter games including: +- Letter Recognition Game +- Combined Letter Games (Letter Hunt, Quick Sight, Memory Challenge) + +## Audio Quality Guidelines + +- Format: WAV files +- Sample Rate: 44.1 kHz or 48 kHz +- Bit Depth: 16-bit or 24-bit +- Duration: 1-3 seconds per letter/syllable +- Clear pronunciation with native Kannada speaker +- Consistent volume levels across all files diff --git "a/public/audio/kannada/letter/\340\262\205.wav" "b/public/audio/kannada/letter/\340\262\205.wav" new file mode 100644 index 00000000..c2866732 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\205.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\205\340\262\202.wav" "b/public/audio/kannada/letter/\340\262\205\340\262\202.wav" new file mode 100644 index 00000000..064c2ba7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\205\340\262\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\205\340\262\203.wav" "b/public/audio/kannada/letter/\340\262\205\340\262\203.wav" new file mode 100644 index 00000000..1f403c6c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\205\340\262\203.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\206.wav" "b/public/audio/kannada/letter/\340\262\206.wav" new file mode 100644 index 00000000..eb9ab22f Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\207.wav" "b/public/audio/kannada/letter/\340\262\207.wav" new file mode 100644 index 00000000..429b17cb Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\210.wav" "b/public/audio/kannada/letter/\340\262\210.wav" new file mode 100644 index 00000000..6df96dfc Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\210.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\211.wav" "b/public/audio/kannada/letter/\340\262\211.wav" new file mode 100644 index 00000000..701bf250 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\211.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\212.wav" "b/public/audio/kannada/letter/\340\262\212.wav" new file mode 100644 index 00000000..f6938e65 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\212.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\213.wav" "b/public/audio/kannada/letter/\340\262\213.wav" new file mode 100644 index 00000000..db7d1bb0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\216.wav" "b/public/audio/kannada/letter/\340\262\216.wav" new file mode 100644 index 00000000..1533fc07 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\216.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\217.wav" "b/public/audio/kannada/letter/\340\262\217.wav" new file mode 100644 index 00000000..8e62f9a6 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\217.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\220.wav" "b/public/audio/kannada/letter/\340\262\220.wav" new file mode 100644 index 00000000..e39f35c1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\220.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\222.wav" "b/public/audio/kannada/letter/\340\262\222.wav" new file mode 100644 index 00000000..9aa56cca Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\222.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\223.wav" "b/public/audio/kannada/letter/\340\262\223.wav" new file mode 100644 index 00000000..10d47835 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\223.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\224.wav" "b/public/audio/kannada/letter/\340\262\224.wav" new file mode 100644 index 00000000..bf097750 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\224.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225.wav" "b/public/audio/kannada/letter/\340\262\225.wav" new file mode 100644 index 00000000..58109a61 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\262\202.wav" "b/public/audio/kannada/letter/\340\262\225\340\262\202.wav" new file mode 100644 index 00000000..96d11f62 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\262\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\225\340\262\276.wav" new file mode 100644 index 00000000..df0a80d3 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\225\340\262\277.wav" new file mode 100644 index 00000000..b9ccdb08 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\201.wav" new file mode 100644 index 00000000..cfb938b3 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\203.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\203.wav" new file mode 100644 index 00000000..541b5bfb Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\203.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\206.wav" new file mode 100644 index 00000000..f72a175b Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\207.wav" new file mode 100644 index 00000000..943524be Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\210.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\210.wav" new file mode 100644 index 00000000..fdde5c87 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\210.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\212.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\212.wav" new file mode 100644 index 00000000..4f713da9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\212.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\225\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\225\340\263\213.wav" new file mode 100644 index 00000000..8ee0ddd4 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\225\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\226.wav" "b/public/audio/kannada/letter/\340\262\226.wav" new file mode 100644 index 00000000..10a4cab1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\226.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227.wav" "b/public/audio/kannada/letter/\340\262\227.wav" new file mode 100644 index 00000000..efee34f1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\227\340\262\276.wav" new file mode 100644 index 00000000..e453c1ee Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\227\340\262\277.wav" new file mode 100644 index 00000000..3ee6a277 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\227\340\263\201.wav" new file mode 100644 index 00000000..877155cd Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\263\202.wav" "b/public/audio/kannada/letter/\340\262\227\340\263\202.wav" new file mode 100644 index 00000000..2e675e57 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\263\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\263\203.wav" "b/public/audio/kannada/letter/\340\262\227\340\263\203.wav" new file mode 100644 index 00000000..6bbf47ce Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\263\203.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\227\340\263\206.wav" new file mode 100644 index 00000000..7846e1d6 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\227\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\227\340\263\214.wav" new file mode 100644 index 00000000..a358f857 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\227\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\230.wav" "b/public/audio/kannada/letter/\340\262\230.wav" new file mode 100644 index 00000000..4fc60ead Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\230.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\231.wav" "b/public/audio/kannada/letter/\340\262\231.wav" new file mode 100644 index 00000000..8858c6dc Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\231.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\232.wav" "b/public/audio/kannada/letter/\340\262\232.wav" new file mode 100644 index 00000000..edbe10a9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\232.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\232\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\232\340\262\277.wav" new file mode 100644 index 00000000..c4ab7275 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\232\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\232\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\232\340\263\213.wav" new file mode 100644 index 00000000..bf56ecb8 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\232\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\233.wav" "b/public/audio/kannada/letter/\340\262\233.wav" new file mode 100644 index 00000000..30a7f3a0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\233.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234.wav" "b/public/audio/kannada/letter/\340\262\234.wav" new file mode 100644 index 00000000..3b6e08f9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\234\340\262\277.wav" new file mode 100644 index 00000000..9b1b967d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\234\340\263\207.wav" new file mode 100644 index 00000000..15d04013 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234\340\263\210.wav" "b/public/audio/kannada/letter/\340\262\234\340\263\210.wav" new file mode 100644 index 00000000..e5773e1d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234\340\263\210.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234\340\263\212.wav" "b/public/audio/kannada/letter/\340\262\234\340\263\212.wav" new file mode 100644 index 00000000..c747b6b1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234\340\263\212.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\234\340\263\213.wav" new file mode 100644 index 00000000..59ddda5b Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\234\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\234\340\263\214.wav" new file mode 100644 index 00000000..2e89582c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\234\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\235.wav" "b/public/audio/kannada/letter/\340\262\235.wav" new file mode 100644 index 00000000..92d9c035 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\235.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\236.wav" "b/public/audio/kannada/letter/\340\262\236.wav" new file mode 100644 index 00000000..3380aa66 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\236.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\237.wav" "b/public/audio/kannada/letter/\340\262\237.wav" new file mode 100644 index 00000000..913216c9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\237.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\237\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\237\340\262\277.wav" new file mode 100644 index 00000000..956ed2b2 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\237\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\237\340\262\277_1.wav" "b/public/audio/kannada/letter/\340\262\237\340\262\277_1.wav" new file mode 100644 index 00000000..956ed2b2 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\237\340\262\277_1.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\237\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\237\340\263\201.wav" new file mode 100644 index 00000000..d010f1e1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\237\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\240.wav" "b/public/audio/kannada/letter/\340\262\240.wav" new file mode 100644 index 00000000..d7c8438c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\240.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\241.wav" "b/public/audio/kannada/letter/\340\262\241.wav" new file mode 100644 index 00000000..28236d1d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\241.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\241\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\241\340\262\277.wav" new file mode 100644 index 00000000..c5822e5d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\241\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\241\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\241\340\263\201.wav" new file mode 100644 index 00000000..bc8165cf Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\241\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\241\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\241\340\263\206.wav" new file mode 100644 index 00000000..dc26f5c4 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\241\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\241\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\241\340\263\213.wav" new file mode 100644 index 00000000..3447b8a9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\241\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\242.wav" "b/public/audio/kannada/letter/\340\262\242.wav" new file mode 100644 index 00000000..c0a24533 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\242.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\243.wav" "b/public/audio/kannada/letter/\340\262\243.wav" new file mode 100644 index 00000000..59c6deed Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\243.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\243\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\243\340\262\277.wav" new file mode 100644 index 00000000..a2394450 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\243\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\244.wav" "b/public/audio/kannada/letter/\340\262\244.wav" new file mode 100644 index 00000000..e64f74f7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\244.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\244\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\244\340\262\277.wav" new file mode 100644 index 00000000..3de94d58 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\244\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\244\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\244\340\263\201.wav" new file mode 100644 index 00000000..18a2cdce Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\244\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\244\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\244\340\263\206.wav" new file mode 100644 index 00000000..d22dab49 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\244\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\244\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\244\340\263\214.wav" new file mode 100644 index 00000000..01d455c9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\244\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\244\340\263\215\340\262\244\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\244\340\263\215\340\262\244\340\263\201.wav" new file mode 100644 index 00000000..d5dfe3bd Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\244\340\263\215\340\262\244\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\245.wav" "b/public/audio/kannada/letter/\340\262\245.wav" new file mode 100644 index 00000000..12e172c7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\245.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246.wav" "b/public/audio/kannada/letter/\340\262\246.wav" new file mode 100644 index 00000000..67e124f9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\246\340\262\276.wav" new file mode 100644 index 00000000..d8ab5919 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\246\340\262\277.wav" new file mode 100644 index 00000000..b6f444d5 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\246\340\263\201.wav" new file mode 100644 index 00000000..58e915f0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\246\340\263\206.wav" new file mode 100644 index 00000000..0e15fea9 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\246\340\263\207.wav" new file mode 100644 index 00000000..94d8de3d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\246\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\246\340\263\214.wav" new file mode 100644 index 00000000..bd54e6d7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\246\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\247.wav" "b/public/audio/kannada/letter/\340\262\247.wav" new file mode 100644 index 00000000..5a40efb3 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\247.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250.wav" "b/public/audio/kannada/letter/\340\262\250.wav" new file mode 100644 index 00000000..47bcaac1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\262\202.wav" "b/public/audio/kannada/letter/\340\262\250\340\262\202.wav" new file mode 100644 index 00000000..faea829b Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\262\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\250\340\262\276.wav" new file mode 100644 index 00000000..029da93c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\250\340\262\277.wav" new file mode 100644 index 00000000..d56ce4c0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\263\200.wav" "b/public/audio/kannada/letter/\340\262\250\340\263\200.wav" new file mode 100644 index 00000000..8b074b5c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\263\200.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\250\340\263\201.wav" new file mode 100644 index 00000000..4476e4b4 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\250\340\263\206.wav" new file mode 100644 index 00000000..f444ccf0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\250\340\263\213.wav" new file mode 100644 index 00000000..e6c101a4 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\250\340\263\214.wav" new file mode 100644 index 00000000..5e8ce2d0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\250\340\263\215\340\262\250\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\250\340\263\215\340\262\250\340\263\201.wav" new file mode 100644 index 00000000..9160abab Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\250\340\263\215\340\262\250\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\252.wav" "b/public/audio/kannada/letter/\340\262\252.wav" new file mode 100644 index 00000000..bccd051a Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\252.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\252\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\252\340\262\276.wav" new file mode 100644 index 00000000..eaae56e8 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\252\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\252\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\252\340\262\277.wav" new file mode 100644 index 00000000..f7af790c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\252\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\252\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\252\340\263\201.wav" new file mode 100644 index 00000000..71a0dd02 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\252\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\252\340\263\210.wav" "b/public/audio/kannada/letter/\340\262\252\340\263\210.wav" new file mode 100644 index 00000000..fd958f15 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\252\340\263\210.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\253.wav" "b/public/audio/kannada/letter/\340\262\253.wav" new file mode 100644 index 00000000..c6e8336a Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\253.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\254.wav" "b/public/audio/kannada/letter/\340\262\254.wav" new file mode 100644 index 00000000..a3e59432 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\254.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\254\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\254\340\262\276.wav" new file mode 100644 index 00000000..965308fb Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\254\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\254\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\254\340\262\277.wav" new file mode 100644 index 00000000..c7960bf3 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\254\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\254\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\254\340\263\207.wav" new file mode 100644 index 00000000..cc81e639 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\254\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\255.wav" "b/public/audio/kannada/letter/\340\262\255.wav" new file mode 100644 index 00000000..d5a0d582 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\255.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256.wav" "b/public/audio/kannada/letter/\340\262\256.wav" new file mode 100644 index 00000000..94ca70cc Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\256\340\262\276.wav" new file mode 100644 index 00000000..6729c88f Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\256\340\263\201.wav" new file mode 100644 index 00000000..9d97cac7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\263\202.wav" "b/public/audio/kannada/letter/\340\262\256\340\263\202.wav" new file mode 100644 index 00000000..0a8a2bdb Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\263\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\256\340\263\206.wav" new file mode 100644 index 00000000..1f9a3c69 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\263\212.wav" "b/public/audio/kannada/letter/\340\262\256\340\263\212.wav" new file mode 100644 index 00000000..91a2eb15 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\263\212.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\256\340\263\213.wav" new file mode 100644 index 00000000..171e3ee0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\256\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\256\340\263\214.wav" new file mode 100644 index 00000000..4998d6ca Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\256\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\257.wav" "b/public/audio/kannada/letter/\340\262\257.wav" new file mode 100644 index 00000000..3fcae783 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\257.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\257\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\257\340\262\276.wav" new file mode 100644 index 00000000..d61d4e46 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\257\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\257\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\257\340\262\277.wav" new file mode 100644 index 00000000..40452db5 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\257\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\257\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\257\340\263\201.wav" new file mode 100644 index 00000000..374671a8 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\257\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260.wav" "b/public/audio/kannada/letter/\340\262\260.wav" new file mode 100644 index 00000000..a61cd5b1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\260\340\262\276.wav" new file mode 100644 index 00000000..bd18cceb Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\260\340\262\277.wav" new file mode 100644 index 00000000..719c648c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\260\340\263\201.wav" new file mode 100644 index 00000000..8c66cb9a Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260\340\263\202.wav" "b/public/audio/kannada/letter/\340\262\260\340\263\202.wav" new file mode 100644 index 00000000..4ee62713 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260\340\263\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\260\340\263\206.wav" new file mode 100644 index 00000000..2e87281c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\260\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\260\340\263\213.wav" new file mode 100644 index 00000000..35f1f8f7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\260\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\262.wav" "b/public/audio/kannada/letter/\340\262\262.wav" new file mode 100644 index 00000000..4114d2e7 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\262.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\262\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\262\340\262\276.wav" new file mode 100644 index 00000000..4a1c04e5 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\262\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\262\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\262\340\262\277.wav" new file mode 100644 index 00000000..ab56940d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\262\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\262\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\262\340\263\201.wav" new file mode 100644 index 00000000..45b088b1 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\262\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\262\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\262\340\263\206.wav" new file mode 100644 index 00000000..8454ee43 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\262\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\262\340\263\215\340\262\262\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\262\340\263\215\340\262\262\340\262\277.wav" new file mode 100644 index 00000000..fa01b50b Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\262\340\263\215\340\262\262\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\263.wav" "b/public/audio/kannada/letter/\340\262\263.wav" new file mode 100644 index 00000000..41f7f4af Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\263.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\263\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\263\340\262\277.wav" new file mode 100644 index 00000000..401aaf0e Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\263\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\263\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\263\340\263\201.wav" new file mode 100644 index 00000000..b17233b2 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\263\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\263\340\263\206.wav" "b/public/audio/kannada/letter/\340\262\263\340\263\206.wav" new file mode 100644 index 00000000..37964f16 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\263\340\263\206.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265.wav" "b/public/audio/kannada/letter/\340\262\265.wav" new file mode 100644 index 00000000..e658fb39 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\265\340\262\276.wav" new file mode 100644 index 00000000..62322b5d Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\265\340\262\277.wav" new file mode 100644 index 00000000..1062f3ae Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\265\340\263\201.wav" new file mode 100644 index 00000000..644e24e0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265\340\263\201_1.wav" "b/public/audio/kannada/letter/\340\262\265\340\263\201_1.wav" new file mode 100644 index 00000000..644e24e0 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265\340\263\201_1.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\265\340\263\207.wav" new file mode 100644 index 00000000..3667a82b Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\265\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\265\340\263\213.wav" new file mode 100644 index 00000000..b5c61e4a Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\265\340\263\213.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\266.wav" "b/public/audio/kannada/letter/\340\262\266.wav" new file mode 100644 index 00000000..5248b53f Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\266.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\267.wav" "b/public/audio/kannada/letter/\340\262\267.wav" new file mode 100644 index 00000000..b120366c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\267.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270.wav" "b/public/audio/kannada/letter/\340\262\270.wav" new file mode 100644 index 00000000..8be821fc Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\270\340\262\276.wav" new file mode 100644 index 00000000..35000a63 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270\340\262\277.wav" "b/public/audio/kannada/letter/\340\262\270\340\262\277.wav" new file mode 100644 index 00000000..4ad7ae6b Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270\340\262\277.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\270\340\263\201.wav" new file mode 100644 index 00000000..7212398f Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270\340\263\202.wav" "b/public/audio/kannada/letter/\340\262\270\340\263\202.wav" new file mode 100644 index 00000000..e2d5c047 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270\340\263\202.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\270\340\263\207.wav" new file mode 100644 index 00000000..e6b95cc5 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\270\340\263\214.wav" "b/public/audio/kannada/letter/\340\262\270\340\263\214.wav" new file mode 100644 index 00000000..fc91df9f Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\270\340\263\214.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\271.wav" "b/public/audio/kannada/letter/\340\262\271.wav" new file mode 100644 index 00000000..b9763374 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\271.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\271\340\262\276.wav" "b/public/audio/kannada/letter/\340\262\271\340\262\276.wav" new file mode 100644 index 00000000..b301699e Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\271\340\262\276.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\271\340\263\201.wav" "b/public/audio/kannada/letter/\340\262\271\340\263\201.wav" new file mode 100644 index 00000000..07e63120 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\271\340\263\201.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\271\340\263\207.wav" "b/public/audio/kannada/letter/\340\262\271\340\263\207.wav" new file mode 100644 index 00000000..1cdee57a Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\271\340\263\207.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\271\340\263\212.wav" "b/public/audio/kannada/letter/\340\262\271\340\263\212.wav" new file mode 100644 index 00000000..520a571c Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\271\340\263\212.wav" differ diff --git "a/public/audio/kannada/letter/\340\262\271\340\263\213.wav" "b/public/audio/kannada/letter/\340\262\271\340\263\213.wav" new file mode 100644 index 00000000..0dcea266 Binary files /dev/null and "b/public/audio/kannada/letter/\340\262\271\340\263\213.wav" differ diff --git a/public/audio/letter-hunt-incorrect-message/en/feedback1.wav b/public/audio/letter-hunt-incorrect-message/en/feedback1.wav new file mode 100644 index 00000000..94e08ca2 Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/en/feedback1.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/en/feedback2.wav b/public/audio/letter-hunt-incorrect-message/en/feedback2.wav new file mode 100644 index 00000000..c6cde852 Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/en/feedback2.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/hi/feedback1.wav b/public/audio/letter-hunt-incorrect-message/hi/feedback1.wav new file mode 100644 index 00000000..3f406386 Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/hi/feedback1.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/hi/feedback2.wav b/public/audio/letter-hunt-incorrect-message/hi/feedback2.wav new file mode 100644 index 00000000..47f0b462 Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/hi/feedback2.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/kn/feedback1.wav b/public/audio/letter-hunt-incorrect-message/kn/feedback1.wav new file mode 100644 index 00000000..5d88f3eb Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/kn/feedback1.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/kn/feedback2.wav b/public/audio/letter-hunt-incorrect-message/kn/feedback2.wav new file mode 100644 index 00000000..9caebc9a Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/kn/feedback2.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/mr/feedback1.wav b/public/audio/letter-hunt-incorrect-message/mr/feedback1.wav new file mode 100644 index 00000000..1ea518db Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/mr/feedback1.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/mr/feedback2.wav b/public/audio/letter-hunt-incorrect-message/mr/feedback2.wav new file mode 100644 index 00000000..152a101d Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/mr/feedback2.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/te/feedback1.wav b/public/audio/letter-hunt-incorrect-message/te/feedback1.wav new file mode 100644 index 00000000..2508414f Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/te/feedback1.wav differ diff --git a/public/audio/letter-hunt-incorrect-message/te/feedback2.wav b/public/audio/letter-hunt-incorrect-message/te/feedback2.wav new file mode 100644 index 00000000..b958e02b Binary files /dev/null and b/public/audio/letter-hunt-incorrect-message/te/feedback2.wav differ diff --git "a/public/audio/marathi/letter/\340\244\205.wav" "b/public/audio/marathi/letter/\340\244\205.wav" new file mode 100644 index 00000000..8a45d798 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\205.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\205\340\244\202.wav" "b/public/audio/marathi/letter/\340\244\205\340\244\202.wav" new file mode 100644 index 00000000..daf33b8a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\205\340\244\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\205\340\244\203.wav" "b/public/audio/marathi/letter/\340\244\205\340\244\203.wav" new file mode 100644 index 00000000..5b940a2f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\205\340\244\203.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\206.wav" "b/public/audio/marathi/letter/\340\244\206.wav" new file mode 100644 index 00000000..577f039c Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\206.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\207.wav" "b/public/audio/marathi/letter/\340\244\207.wav" new file mode 100644 index 00000000..8c4b83bf Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\210.wav" "b/public/audio/marathi/letter/\340\244\210.wav" new file mode 100644 index 00000000..d41753e0 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\210.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\211.wav" "b/public/audio/marathi/letter/\340\244\211.wav" new file mode 100644 index 00000000..77606062 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\211.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\212.wav" "b/public/audio/marathi/letter/\340\244\212.wav" new file mode 100644 index 00000000..d5b3e950 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\212.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\213.wav" "b/public/audio/marathi/letter/\340\244\213.wav" new file mode 100644 index 00000000..9c656730 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\217.wav" "b/public/audio/marathi/letter/\340\244\217.wav" new file mode 100644 index 00000000..89c0cde1 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\217.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\220.wav" "b/public/audio/marathi/letter/\340\244\220.wav" new file mode 100644 index 00000000..2661f140 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\220.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\223.wav" "b/public/audio/marathi/letter/\340\244\223.wav" new file mode 100644 index 00000000..a41542ee Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\223.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\224.wav" "b/public/audio/marathi/letter/\340\244\224.wav" new file mode 100644 index 00000000..5f54782a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\224.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225.wav" "b/public/audio/marathi/letter/\340\244\225.wav" new file mode 100644 index 00000000..9fe7d48e Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\244\202.wav" "b/public/audio/marathi/letter/\340\244\225\340\244\202.wav" new file mode 100644 index 00000000..3cdd74a0 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\244\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\225\340\244\276.wav" new file mode 100644 index 00000000..61baab7a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\244\277.wav" "b/public/audio/marathi/letter/\340\244\225\340\244\277.wav" new file mode 100644 index 00000000..fa9e44c9 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\244\277.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\200.wav" new file mode 100644 index 00000000..ef2220c4 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\201.wav" new file mode 100644 index 00000000..c61de644 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\202.wav" new file mode 100644 index 00000000..b77e2bcd Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\203.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\203.wav" new file mode 100644 index 00000000..e591c682 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\203.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\207.wav" new file mode 100644 index 00000000..c0b55aff Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\210.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\210.wav" new file mode 100644 index 00000000..3cd2339f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\210.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\213.wav" new file mode 100644 index 00000000..96c539ae Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\214.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\214.wav" new file mode 100644 index 00000000..0f697c2e Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\214.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\225\340\245\215\340\244\267.wav" "b/public/audio/marathi/letter/\340\244\225\340\245\215\340\244\267.wav" new file mode 100644 index 00000000..8bdadbcd Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\225\340\245\215\340\244\267.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\226.wav" "b/public/audio/marathi/letter/\340\244\226.wav" new file mode 100644 index 00000000..bc1092c8 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\226.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227.wav" "b/public/audio/marathi/letter/\340\244\227.wav" new file mode 100644 index 00000000..68076412 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\244\202.wav" "b/public/audio/marathi/letter/\340\244\227\340\244\202.wav" new file mode 100644 index 00000000..ab47befe Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\244\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\227\340\244\276.wav" new file mode 100644 index 00000000..572d920f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\244\277.wav" "b/public/audio/marathi/letter/\340\244\227\340\244\277.wav" new file mode 100644 index 00000000..10cf5a2d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\244\277.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\200.wav" new file mode 100644 index 00000000..f1ee88c2 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\201.wav" new file mode 100644 index 00000000..c858cdaa Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\202.wav" new file mode 100644 index 00000000..9e396f56 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\203.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\203.wav" new file mode 100644 index 00000000..a9af309f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\203.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\207.wav" new file mode 100644 index 00000000..301f5821 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\210.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\210.wav" new file mode 100644 index 00000000..c217cf82 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\210.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\213.wav" new file mode 100644 index 00000000..c4fba3c6 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\227\340\245\214.wav" "b/public/audio/marathi/letter/\340\244\227\340\245\214.wav" new file mode 100644 index 00000000..4807c3d2 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\227\340\245\214.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\230.wav" "b/public/audio/marathi/letter/\340\244\230.wav" new file mode 100644 index 00000000..76d62761 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\230.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\232.wav" "b/public/audio/marathi/letter/\340\244\232.wav" new file mode 100644 index 00000000..1bf567dd Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\232.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\233.wav" "b/public/audio/marathi/letter/\340\244\233.wav" new file mode 100644 index 00000000..9bec0e88 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\233.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\234.wav" "b/public/audio/marathi/letter/\340\244\234.wav" new file mode 100644 index 00000000..d18b66e7 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\234.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\234\340\245\215\340\244\236.wav" "b/public/audio/marathi/letter/\340\244\234\340\245\215\340\244\236.wav" new file mode 100644 index 00000000..64f54c7b Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\234\340\245\215\340\244\236.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\235.wav" "b/public/audio/marathi/letter/\340\244\235.wav" new file mode 100644 index 00000000..2ce24d79 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\235.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\237.wav" "b/public/audio/marathi/letter/\340\244\237.wav" new file mode 100644 index 00000000..34d5697d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\237.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\240.wav" "b/public/audio/marathi/letter/\340\244\240.wav" new file mode 100644 index 00000000..04952b19 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\240.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\241.wav" "b/public/audio/marathi/letter/\340\244\241.wav" new file mode 100644 index 00000000..4e4efa71 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\241.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\242.wav" "b/public/audio/marathi/letter/\340\244\242.wav" new file mode 100644 index 00000000..7c39992a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\242.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\243.wav" "b/public/audio/marathi/letter/\340\244\243.wav" new file mode 100644 index 00000000..40af0007 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\243.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244.wav" "b/public/audio/marathi/letter/\340\244\244.wav" new file mode 100644 index 00000000..07dab10a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\244\340\244\276.wav" new file mode 100644 index 00000000..a95fcf70 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\244\277.wav" "b/public/audio/marathi/letter/\340\244\244\340\244\277.wav" new file mode 100644 index 00000000..09a2701d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\244\277.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\244\340\245\200.wav" new file mode 100644 index 00000000..ccae26b8 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\244\340\245\201.wav" new file mode 100644 index 00000000..13d6e3a7 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\244\340\245\202.wav" new file mode 100644 index 00000000..4833785f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\244\340\245\207.wav" new file mode 100644 index 00000000..9710ec2f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\245\210.wav" "b/public/audio/marathi/letter/\340\244\244\340\245\210.wav" new file mode 100644 index 00000000..e5c141df Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\245\210.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\244\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\244\340\245\213.wav" new file mode 100644 index 00000000..ec35b03d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\244\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\245.wav" "b/public/audio/marathi/letter/\340\244\245.wav" new file mode 100644 index 00000000..f5e5a951 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\245.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\246.wav" "b/public/audio/marathi/letter/\340\244\246.wav" new file mode 100644 index 00000000..78aee197 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\246.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\247.wav" "b/public/audio/marathi/letter/\340\244\247.wav" new file mode 100644 index 00000000..ad69ac70 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\247.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250.wav" "b/public/audio/marathi/letter/\340\244\250.wav" new file mode 100644 index 00000000..286c4484 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\250\340\244\276.wav" new file mode 100644 index 00000000..e1e16590 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\244\277.wav" "b/public/audio/marathi/letter/\340\244\250\340\244\277.wav" new file mode 100644 index 00000000..60801d7a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\244\277.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\250\340\245\200.wav" new file mode 100644 index 00000000..07d19100 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\250\340\245\201.wav" new file mode 100644 index 00000000..8bb41e4a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\250\340\245\202.wav" new file mode 100644 index 00000000..8969e86e Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\250\340\245\207.wav" new file mode 100644 index 00000000..b3dd985a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\245\210.wav" "b/public/audio/marathi/letter/\340\244\250\340\245\210.wav" new file mode 100644 index 00000000..798c652d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\245\210.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\250\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\250\340\245\213.wav" new file mode 100644 index 00000000..f5725b0f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\250\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\252.wav" "b/public/audio/marathi/letter/\340\244\252.wav" new file mode 100644 index 00000000..93e89364 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\252.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\253.wav" "b/public/audio/marathi/letter/\340\244\253.wav" new file mode 100644 index 00000000..5207fe0b Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\253.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\254.wav" "b/public/audio/marathi/letter/\340\244\254.wav" new file mode 100644 index 00000000..86c30390 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\254.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\255.wav" "b/public/audio/marathi/letter/\340\244\255.wav" new file mode 100644 index 00000000..fcb57a95 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\255.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256.wav" "b/public/audio/marathi/letter/\340\244\256.wav" new file mode 100644 index 00000000..517f9088 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\256\340\244\276.wav" new file mode 100644 index 00000000..bcae5c0a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\244\277.wav" "b/public/audio/marathi/letter/\340\244\256\340\244\277.wav" new file mode 100644 index 00000000..f9e83796 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\244\277.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\256\340\245\200.wav" new file mode 100644 index 00000000..3b89f18d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\256\340\245\201.wav" new file mode 100644 index 00000000..aa1acd12 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\256\340\245\202.wav" new file mode 100644 index 00000000..93fc2992 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\256\340\245\207.wav" new file mode 100644 index 00000000..b4acb297 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\245\210.wav" "b/public/audio/marathi/letter/\340\244\256\340\245\210.wav" new file mode 100644 index 00000000..00684ba1 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\245\210.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\256\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\256\340\245\213.wav" new file mode 100644 index 00000000..8a206ad2 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\256\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\257.wav" "b/public/audio/marathi/letter/\340\244\257.wav" new file mode 100644 index 00000000..a7c18e67 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\257.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\257\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\257\340\244\276.wav" new file mode 100644 index 00000000..1ccc0fe0 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\257\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260.wav" "b/public/audio/marathi/letter/\340\244\260.wav" new file mode 100644 index 00000000..0346071d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\260\340\244\276.wav" new file mode 100644 index 00000000..cf7b7840 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\260\340\245\200.wav" new file mode 100644 index 00000000..ee55ffcf Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\260\340\245\201.wav" new file mode 100644 index 00000000..b2926a26 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\260\340\245\202.wav" new file mode 100644 index 00000000..ae988583 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\260\340\245\207.wav" new file mode 100644 index 00000000..ff86aa0b Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\260\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\260\340\245\213.wav" new file mode 100644 index 00000000..6bff5c73 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\260\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262.wav" "b/public/audio/marathi/letter/\340\244\262.wav" new file mode 100644 index 00000000..40355751 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\262\340\244\276.wav" new file mode 100644 index 00000000..18b4ca06 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262\340\245\200.wav" "b/public/audio/marathi/letter/\340\244\262\340\245\200.wav" new file mode 100644 index 00000000..5b148356 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262\340\245\200.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\262\340\245\201.wav" new file mode 100644 index 00000000..da0ae670 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262\340\245\202.wav" "b/public/audio/marathi/letter/\340\244\262\340\245\202.wav" new file mode 100644 index 00000000..7e9e28f5 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262\340\245\202.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\262\340\245\207.wav" new file mode 100644 index 00000000..c47a5276 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\262\340\245\213.wav" "b/public/audio/marathi/letter/\340\244\262\340\245\213.wav" new file mode 100644 index 00000000..dbc7fc7f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\262\340\245\213.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\263.wav" "b/public/audio/marathi/letter/\340\244\263.wav" new file mode 100644 index 00000000..aaef8fb1 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\263.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\265.wav" "b/public/audio/marathi/letter/\340\244\265.wav" new file mode 100644 index 00000000..7ab4ce9a Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\265.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\265\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\265\340\244\276.wav" new file mode 100644 index 00000000..00a5476d Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\265\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\266.wav" "b/public/audio/marathi/letter/\340\244\266.wav" new file mode 100644 index 00000000..a4d9885f Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\266.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\267.wav" "b/public/audio/marathi/letter/\340\244\267.wav" new file mode 100644 index 00000000..b41bc185 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\267.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\270.wav" "b/public/audio/marathi/letter/\340\244\270.wav" new file mode 100644 index 00000000..d762eb4e Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\270.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\270\340\244\276.wav" "b/public/audio/marathi/letter/\340\244\270\340\244\276.wav" new file mode 100644 index 00000000..4ae96c47 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\270\340\244\276.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\270\340\245\201.wav" "b/public/audio/marathi/letter/\340\244\270\340\245\201.wav" new file mode 100644 index 00000000..b669ded4 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\270\340\245\201.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\270\340\245\207.wav" "b/public/audio/marathi/letter/\340\244\270\340\245\207.wav" new file mode 100644 index 00000000..f3070f8c Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\270\340\245\207.wav" differ diff --git "a/public/audio/marathi/letter/\340\244\271.wav" "b/public/audio/marathi/letter/\340\244\271.wav" new file mode 100644 index 00000000..3e8fb905 Binary files /dev/null and "b/public/audio/marathi/letter/\340\244\271.wav" differ diff --git a/public/audio/telugu/README.md b/public/audio/telugu/README.md new file mode 100644 index 00000000..dcf94f3e --- /dev/null +++ b/public/audio/telugu/README.md @@ -0,0 +1,150 @@ +# Telugu Audio Files Setup + +## 📁 Audio Folder Location +Place your Telugu audio files in: `public/audio/telugu/` + +## 🎵 File Naming Convention +Name your audio files exactly as the Telugu letters appear in the game: + +### Level 1 Files: +- `అ.wav` +- `ఆ.wav` +- `ఉ.wav` +- `ఎ.wav` +- `ఏ.wav` +- `ఇ.wav` +- `ఒ.wav` +- `అం.wav` +- `ఈ.wav` +- `ఊ.wav` +- `ఓ.wav` + +### Level 2 Files: +- `ఐ.wav` +- `ఔ.wav` +- `ఋ.wav` +- `న.wav` +- `ల.wav` +- `క.wav` +- `ర.wav` +- `ప.wav` +- `త.wav` +- `ద.wav` + +### Level 3 Files: +- `మ.wav` +- `వ.wav` +- `డ.wav` +- `చ.wav` +- `గ.wav` +- `ట.wav` +- `స.wav` +- `య.wav` +- `బ.wav` +- `జ.wav` + +### Level 4 Files: +- `ష.wav` +- `శ.wav` +- `ళ.wav` +- `ణ.wav` +- `హ.wav` +- `ధ.wav` +- `భ.wav` +- `థ.wav` +- `ఠ.wav` +- `ఖ.wav` + +### Level 5 Files: +- `ఫ.wav` +- `ఘ.wav` +- `ఞ.wav` +- `ఛ.wav` +- `ఢ.wav` +- `ఝ.wav` +- `ఱ.wav` +- `లు.wav` +- `ని.wav` +- `ది.wav` + +### Level 6 Files: +- `ను.wav` +- `కు.wav` +- `లో.wav` +- `డి.wav` +- `రా.wav` +- `వా.wav` +- `గా.wav` +- `దా.wav` +- `రు.wav` +- `డు.wav` + +### Level 7 Files: +- `రి.wav` +- `గు.wav` +- `కా.wav` +- `చి.wav` +- `చేద.wav` +- `చె.wav` +- `పా.wav` +- `వియి.wav` +- `కి.wav` +- `దిు.wav` + +### Level 8 Files: +- `మా.wav` +- `లి.wav` +- `నా.wav` +- `వు.wav` +- `తో.wav` +- `న్నా.wav` +- `టి.wav` +- `పు.wav` +- `ము.wav` +- `సి.wav` + +### Level 9 Files: +- `యి.wav` +- `తి.wav` +- `నే.wav` +- `తా.wav` +- `తు.wav` +- `తె.wav` +- `పి.wav` +- `చూ.wav` +- `డా.wav` +- `కొ.wav` + +### Level 10 Files: +- `మీ.wav` +- `పో.wav` +- `సు.wav` +- `కిం.wav` +- `మై.wav` +- `కృ.wav` +- `గౌ.wav` + +## 🔧 How It Works + +1. **Automatic Detection**: The game automatically detects if audio files exist for each letter +2. **Fallback System**: If an audio file is missing, the game falls back to text-to-speech +3. **No Configuration Needed**: Just add the files to the folder and they'll be used automatically + +## 📝 Notes + +- **File Format**: Currently supports `.wav` files +- **File Size**: Keep files reasonably sized for web performance +- **Quality**: Use clear, high-quality audio for best learning experience +- **Missing Files**: If a file is missing, the game will use text-to-speech as fallback + +## 🚀 Quick Start + +1. Create the folder: `public/audio/telugu/` +2. Add your `.wav` files with exact letter names +3. Start the game - audio will work automatically! + +## 🔍 Troubleshooting + +- **Audio not playing**: Check file names match exactly (including special characters) +- **File not found**: Ensure files are in `public/audio/telugu/` folder +- **Wrong pronunciation**: Verify file names match the Telugu letters exactly diff --git "a/public/audio/telugu/letter/\340\260\205.wav" "b/public/audio/telugu/letter/\340\260\205.wav" new file mode 100644 index 00000000..c2866732 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\205.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\205\340\260\202.wav" "b/public/audio/telugu/letter/\340\260\205\340\260\202.wav" new file mode 100644 index 00000000..064c2ba7 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\205\340\260\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\205\340\260\203.wav" "b/public/audio/telugu/letter/\340\260\205\340\260\203.wav" new file mode 100644 index 00000000..1f403c6c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\205\340\260\203.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\206.wav" "b/public/audio/telugu/letter/\340\260\206.wav" new file mode 100644 index 00000000..eb9ab22f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\207.wav" "b/public/audio/telugu/letter/\340\260\207.wav" new file mode 100644 index 00000000..429b17cb Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\210.wav" "b/public/audio/telugu/letter/\340\260\210.wav" new file mode 100644 index 00000000..6df96dfc Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\211.wav" "b/public/audio/telugu/letter/\340\260\211.wav" new file mode 100644 index 00000000..701bf250 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\211.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\212.wav" "b/public/audio/telugu/letter/\340\260\212.wav" new file mode 100644 index 00000000..f6938e65 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\212.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\213.wav" "b/public/audio/telugu/letter/\340\260\213.wav" new file mode 100644 index 00000000..db7d1bb0 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\213.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\216.wav" "b/public/audio/telugu/letter/\340\260\216.wav" new file mode 100644 index 00000000..1533fc07 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\216.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\217.wav" "b/public/audio/telugu/letter/\340\260\217.wav" new file mode 100644 index 00000000..8e62f9a6 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\217.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\220.wav" "b/public/audio/telugu/letter/\340\260\220.wav" new file mode 100644 index 00000000..e39f35c1 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\220.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\222.wav" "b/public/audio/telugu/letter/\340\260\222.wav" new file mode 100644 index 00000000..b7b8d8cc Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\222.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\223.wav" "b/public/audio/telugu/letter/\340\260\223.wav" new file mode 100644 index 00000000..77b8d5ae Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\223.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\224.wav" "b/public/audio/telugu/letter/\340\260\224.wav" new file mode 100644 index 00000000..bf097750 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\224.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225.wav" "b/public/audio/telugu/letter/\340\260\225.wav" new file mode 100644 index 00000000..58109a61 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\260\202.wav" "b/public/audio/telugu/letter/\340\260\225\340\260\202.wav" new file mode 100644 index 00000000..7e62d181 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\260\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\225\340\260\276.wav" new file mode 100644 index 00000000..67c80fe6 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\225\340\260\277.wav" new file mode 100644 index 00000000..82923144 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\260\277\340\260\202.wav" "b/public/audio/telugu/letter/\340\260\225\340\260\277\340\260\202.wav" new file mode 100644 index 00000000..b9822702 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\260\277\340\260\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\225\340\261\201.wav" new file mode 100644 index 00000000..1e416206 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\261\203.wav" "b/public/audio/telugu/letter/\340\260\225\340\261\203.wav" new file mode 100644 index 00000000..fec10884 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\261\203.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\261\212.wav" "b/public/audio/telugu/letter/\340\260\225\340\261\212.wav" new file mode 100644 index 00000000..70fc3a6d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\261\212.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\261\213.wav" "b/public/audio/telugu/letter/\340\260\225\340\261\213.wav" new file mode 100644 index 00000000..d350d7be Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\261\213.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\225\340\261\215\340\260\267.wav" "b/public/audio/telugu/letter/\340\260\225\340\261\215\340\260\267.wav" new file mode 100644 index 00000000..f7509816 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\225\340\261\215\340\260\267.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\226.wav" "b/public/audio/telugu/letter/\340\260\226.wav" new file mode 100644 index 00000000..10a4cab1 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\226.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227.wav" "b/public/audio/telugu/letter/\340\260\227.wav" new file mode 100644 index 00000000..efee34f1 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\227\340\260\276.wav" new file mode 100644 index 00000000..965b4d13 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\261\200.wav" "b/public/audio/telugu/letter/\340\260\227\340\261\200.wav" new file mode 100644 index 00000000..97f32c23 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\261\200.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\227\340\261\201.wav" new file mode 100644 index 00000000..49f74bb4 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\261\203.wav" "b/public/audio/telugu/letter/\340\260\227\340\261\203.wav" new file mode 100644 index 00000000..0fe12a4d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\261\203.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\227\340\261\206.wav" new file mode 100644 index 00000000..afa1ab36 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\261\212.wav" "b/public/audio/telugu/letter/\340\260\227\340\261\212.wav" new file mode 100644 index 00000000..b2ebfbcd Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\261\212.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\227\340\261\214.wav" "b/public/audio/telugu/letter/\340\260\227\340\261\214.wav" new file mode 100644 index 00000000..0b75652f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\227\340\261\214.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\230.wav" "b/public/audio/telugu/letter/\340\260\230.wav" new file mode 100644 index 00000000..4fc60ead Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\230.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\231.wav" "b/public/audio/telugu/letter/\340\260\231.wav" new file mode 100644 index 00000000..8858c6dc Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\231.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\232.wav" "b/public/audio/telugu/letter/\340\260\232.wav" new file mode 100644 index 00000000..edbe10a9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\232.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\232\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\232\340\260\277.wav" new file mode 100644 index 00000000..b6d7f6c9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\232\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\232\340\261\200.wav" "b/public/audio/telugu/letter/\340\260\232\340\261\200.wav" new file mode 100644 index 00000000..1c00542d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\232\340\261\200.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\232\340\261\202.wav" "b/public/audio/telugu/letter/\340\260\232\340\261\202.wav" new file mode 100644 index 00000000..dab83b85 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\232\340\261\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\232\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\232\340\261\206.wav" new file mode 100644 index 00000000..4be99768 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\232\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\232\340\261\207.wav" "b/public/audio/telugu/letter/\340\260\232\340\261\207.wav" new file mode 100644 index 00000000..b4211a7c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\232\340\261\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\233.wav" "b/public/audio/telugu/letter/\340\260\233.wav" new file mode 100644 index 00000000..30a7f3a0 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\233.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234.wav" "b/public/audio/telugu/letter/\340\260\234.wav" new file mode 100644 index 00000000..3b6e08f9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\234\340\260\276.wav" new file mode 100644 index 00000000..b0c728d6 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\234\340\260\277.wav" new file mode 100644 index 00000000..dbc92312 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\234\340\261\201.wav" new file mode 100644 index 00000000..a491cae4 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\261\202.wav" "b/public/audio/telugu/letter/\340\260\234\340\261\202.wav" new file mode 100644 index 00000000..cf773778 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\261\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\234\340\261\206.wav" new file mode 100644 index 00000000..918dcb7b Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\261\210.wav" "b/public/audio/telugu/letter/\340\260\234\340\261\210.wav" new file mode 100644 index 00000000..39c86bcd Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\261\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\234\340\261\214.wav" "b/public/audio/telugu/letter/\340\260\234\340\261\214.wav" new file mode 100644 index 00000000..dfb2a899 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\234\340\261\214.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\235.wav" "b/public/audio/telugu/letter/\340\260\235.wav" new file mode 100644 index 00000000..92d9c035 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\235.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\236.wav" "b/public/audio/telugu/letter/\340\260\236.wav" new file mode 100644 index 00000000..6116f1f0 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\236.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\237.wav" "b/public/audio/telugu/letter/\340\260\237.wav" new file mode 100644 index 00000000..913216c9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\237.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\237\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\237\340\260\277.wav" new file mode 100644 index 00000000..8bca8d46 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\237\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\237\340\261\214.wav" "b/public/audio/telugu/letter/\340\260\237\340\261\214.wav" new file mode 100644 index 00000000..6f8fc4f1 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\237\340\261\214.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\240.wav" "b/public/audio/telugu/letter/\340\260\240.wav" new file mode 100644 index 00000000..d7c8438c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\240.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\241.wav" "b/public/audio/telugu/letter/\340\260\241.wav" new file mode 100644 index 00000000..28236d1d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\241.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\241\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\241\340\260\276.wav" new file mode 100644 index 00000000..abea7986 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\241\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\241\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\241\340\260\277.wav" new file mode 100644 index 00000000..40e71be5 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\241\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\241\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\241\340\261\201.wav" new file mode 100644 index 00000000..693a5439 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\241\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\241\340\261\210.wav" "b/public/audio/telugu/letter/\340\260\241\340\261\210.wav" new file mode 100644 index 00000000..2764edd5 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\241\340\261\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\242.wav" "b/public/audio/telugu/letter/\340\260\242.wav" new file mode 100644 index 00000000..9d65a2f0 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\242.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\243.wav" "b/public/audio/telugu/letter/\340\260\243.wav" new file mode 100644 index 00000000..38a1ecd8 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\243.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\244.wav" "b/public/audio/telugu/letter/\340\260\244.wav" new file mode 100644 index 00000000..e64f74f7 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\244.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\244\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\244\340\260\276.wav" new file mode 100644 index 00000000..5ef7c9a5 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\244\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\244\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\244\340\260\277.wav" new file mode 100644 index 00000000..b202bdd7 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\244\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\244\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\244\340\261\201.wav" new file mode 100644 index 00000000..8089f6a1 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\244\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\244\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\244\340\261\206.wav" new file mode 100644 index 00000000..616a8141 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\244\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\244\340\261\213.wav" "b/public/audio/telugu/letter/\340\260\244\340\261\213.wav" new file mode 100644 index 00000000..1b16bb27 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\244\340\261\213.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\245.wav" "b/public/audio/telugu/letter/\340\260\245.wav" new file mode 100644 index 00000000..492d10d9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\245.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\246.wav" "b/public/audio/telugu/letter/\340\260\246.wav" new file mode 100644 index 00000000..67e124f9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\246.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\246\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\246\340\260\276.wav" new file mode 100644 index 00000000..6ae30a78 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\246\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\246\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\246\340\260\277.wav" new file mode 100644 index 00000000..d04aa1e6 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\246\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\246\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\246\340\261\201.wav" new file mode 100644 index 00000000..9f2a49b6 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\246\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\247.wav" "b/public/audio/telugu/letter/\340\260\247.wav" new file mode 100644 index 00000000..5a40efb3 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\247.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250.wav" "b/public/audio/telugu/letter/\340\260\250.wav" new file mode 100644 index 00000000..48a9e00a Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\250\340\260\276.wav" new file mode 100644 index 00000000..6abf965e Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\250\340\260\277.wav" new file mode 100644 index 00000000..e40bb02d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\200.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\200.wav" new file mode 100644 index 00000000..5399e6da Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\200.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\201.wav" new file mode 100644 index 00000000..ddb22f47 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\203.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\203.wav" new file mode 100644 index 00000000..4876ec1c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\203.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\206.wav" new file mode 100644 index 00000000..82e083f7 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\207.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\207.wav" new file mode 100644 index 00000000..f61487c3 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\212.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\212.wav" new file mode 100644 index 00000000..66da9969 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\212.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\214.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\214.wav" new file mode 100644 index 00000000..0422d431 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\214.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\250\340\261\215\340\260\250\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\250\340\261\215\340\260\250\340\260\276.wav" new file mode 100644 index 00000000..1574cd45 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\250\340\261\215\340\260\250\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252.wav" "b/public/audio/telugu/letter/\340\260\252.wav" new file mode 100644 index 00000000..bccd051a Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\252\340\260\276.wav" new file mode 100644 index 00000000..3cac85f2 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\252\340\260\277.wav" new file mode 100644 index 00000000..c38d52eb Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\261\200.wav" "b/public/audio/telugu/letter/\340\260\252\340\261\200.wav" new file mode 100644 index 00000000..43348b8e Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\261\200.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\252\340\261\201.wav" new file mode 100644 index 00000000..4fe98f3d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\252\340\261\206.wav" new file mode 100644 index 00000000..a63e40e3 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\261\210.wav" "b/public/audio/telugu/letter/\340\260\252\340\261\210.wav" new file mode 100644 index 00000000..cc742e5e Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\261\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\252\340\261\213.wav" "b/public/audio/telugu/letter/\340\260\252\340\261\213.wav" new file mode 100644 index 00000000..2a7525d9 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\252\340\261\213.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\253.wav" "b/public/audio/telugu/letter/\340\260\253.wav" new file mode 100644 index 00000000..3fed7d3e Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\253.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\254.wav" "b/public/audio/telugu/letter/\340\260\254.wav" new file mode 100644 index 00000000..a3e59432 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\254.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\254\340\261\200.wav" "b/public/audio/telugu/letter/\340\260\254\340\261\200.wav" new file mode 100644 index 00000000..e02c824f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\254\340\261\200.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\254\340\261\202.wav" "b/public/audio/telugu/letter/\340\260\254\340\261\202.wav" new file mode 100644 index 00000000..0bb80aae Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\254\340\261\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\254\340\261\203.wav" "b/public/audio/telugu/letter/\340\260\254\340\261\203.wav" new file mode 100644 index 00000000..462994dc Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\254\340\261\203.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\254\340\261\207.wav" "b/public/audio/telugu/letter/\340\260\254\340\261\207.wav" new file mode 100644 index 00000000..80a97a5b Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\254\340\261\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\255.wav" "b/public/audio/telugu/letter/\340\260\255.wav" new file mode 100644 index 00000000..d5a0d582 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\255.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256.wav" "b/public/audio/telugu/letter/\340\260\256.wav" new file mode 100644 index 00000000..94ca70cc Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\256\340\260\276.wav" new file mode 100644 index 00000000..0fc77825 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\200.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\200.wav" new file mode 100644 index 00000000..f901b91c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\200.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\201.wav" new file mode 100644 index 00000000..19354c3f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\202.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\202.wav" new file mode 100644 index 00000000..de266fee Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\206.wav" new file mode 100644 index 00000000..7e23c75b Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\207.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\207.wav" new file mode 100644 index 00000000..38f983e2 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\210.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\210.wav" new file mode 100644 index 00000000..d9d67c2f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\256\340\261\212.wav" "b/public/audio/telugu/letter/\340\260\256\340\261\212.wav" new file mode 100644 index 00000000..eb24b657 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\256\340\261\212.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\257.wav" "b/public/audio/telugu/letter/\340\260\257.wav" new file mode 100644 index 00000000..3fcae783 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\257.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\257\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\257\340\260\277.wav" new file mode 100644 index 00000000..1658aedf Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\257\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260.wav" "b/public/audio/telugu/letter/\340\260\260.wav" new file mode 100644 index 00000000..a61cd5b1 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\260\340\260\276.wav" new file mode 100644 index 00000000..a7c52b7d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\260\340\260\277.wav" new file mode 100644 index 00000000..8abf02b6 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\260\340\261\201.wav" new file mode 100644 index 00000000..d657223c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260\340\261\206.wav" "b/public/audio/telugu/letter/\340\260\260\340\261\206.wav" new file mode 100644 index 00000000..c0480d8a Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260\340\261\206.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260\340\261\207.wav" "b/public/audio/telugu/letter/\340\260\260\340\261\207.wav" new file mode 100644 index 00000000..2ae16ced Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260\340\261\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\260\340\261\213.wav" "b/public/audio/telugu/letter/\340\260\260\340\261\213.wav" new file mode 100644 index 00000000..981cb6c4 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\260\340\261\213.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\261.wav" "b/public/audio/telugu/letter/\340\260\261.wav" new file mode 100644 index 00000000..7ad98082 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\261.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\262.wav" "b/public/audio/telugu/letter/\340\260\262.wav" new file mode 100644 index 00000000..4114d2e7 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\262.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\262\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\262\340\260\277.wav" new file mode 100644 index 00000000..2cf31862 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\262\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\262\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\262\340\261\201.wav" new file mode 100644 index 00000000..5cf7d0ac Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\262\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\262\340\261\207.wav" "b/public/audio/telugu/letter/\340\260\262\340\261\207.wav" new file mode 100644 index 00000000..5331f03b Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\262\340\261\207.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\262\340\261\213.wav" "b/public/audio/telugu/letter/\340\260\262\340\261\213.wav" new file mode 100644 index 00000000..aa958208 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\262\340\261\213.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\263.wav" "b/public/audio/telugu/letter/\340\260\263.wav" new file mode 100644 index 00000000..41f7f4af Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\263.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\265.wav" "b/public/audio/telugu/letter/\340\260\265.wav" new file mode 100644 index 00000000..e658fb39 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\265.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\265\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\265\340\260\276.wav" new file mode 100644 index 00000000..eb8fd78f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\265\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\265\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\265\340\260\277.wav" new file mode 100644 index 00000000..4324d2eb Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\265\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\265\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\265\340\261\201.wav" new file mode 100644 index 00000000..1867b43d Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\265\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\265\340\261\210.wav" "b/public/audio/telugu/letter/\340\260\265\340\261\210.wav" new file mode 100644 index 00000000..ffb41b05 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\265\340\261\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\266.wav" "b/public/audio/telugu/letter/\340\260\266.wav" new file mode 100644 index 00000000..5248b53f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\266.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\266\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\266\340\261\201.wav" new file mode 100644 index 00000000..e4535695 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\266\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\266\340\261\210.wav" "b/public/audio/telugu/letter/\340\260\266\340\261\210.wav" new file mode 100644 index 00000000..d8ba60a2 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\266\340\261\210.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\267.wav" "b/public/audio/telugu/letter/\340\260\267.wav" new file mode 100644 index 00000000..b120366c Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\267.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270.wav" "b/public/audio/telugu/letter/\340\260\270.wav" new file mode 100644 index 00000000..8be821fc Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270\340\260\276.wav" "b/public/audio/telugu/letter/\340\260\270\340\260\276.wav" new file mode 100644 index 00000000..2af1fc90 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270\340\260\276.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270\340\260\277.wav" "b/public/audio/telugu/letter/\340\260\270\340\260\277.wav" new file mode 100644 index 00000000..037ce048 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270\340\260\277.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270\340\261\201.wav" "b/public/audio/telugu/letter/\340\260\270\340\261\201.wav" new file mode 100644 index 00000000..478e1c68 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270\340\261\201.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270\340\261\202.wav" "b/public/audio/telugu/letter/\340\260\270\340\261\202.wav" new file mode 100644 index 00000000..e1bf351f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270\340\261\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270\340\261\212.wav" "b/public/audio/telugu/letter/\340\260\270\340\261\212.wav" new file mode 100644 index 00000000..995aa16f Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270\340\261\212.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\270\340\261\214.wav" "b/public/audio/telugu/letter/\340\260\270\340\261\214.wav" new file mode 100644 index 00000000..04d13374 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\270\340\261\214.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\271.wav" "b/public/audio/telugu/letter/\340\260\271.wav" new file mode 100644 index 00000000..b9763374 Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\271.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\271\340\260\202.wav" "b/public/audio/telugu/letter/\340\260\271\340\260\202.wav" new file mode 100644 index 00000000..d3ee275b Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\271\340\260\202.wav" differ diff --git "a/public/audio/telugu/letter/\340\260\271\340\261\203.wav" "b/public/audio/telugu/letter/\340\260\271\340\261\203.wav" new file mode 100644 index 00000000..62c87dda Binary files /dev/null and "b/public/audio/telugu/letter/\340\260\271\340\261\203.wav" differ diff --git a/public/images/moon.png b/public/images/moon.png new file mode 100644 index 00000000..e87e0460 Binary files /dev/null and b/public/images/moon.png differ diff --git a/public/index.html b/public/index.html index 368bebf0..53990d49 100644 --- a/public/index.html +++ b/public/index.html @@ -23,6 +23,8 @@ /> + + EkStep diff --git a/src/App.js b/src/App.js index 3032f940..776ee679 100644 --- a/src/App.js +++ b/src/App.js @@ -7,11 +7,45 @@ import { AppContent } from "./views"; import theme from "./assets/styles/theme"; import "@tekdi/all-telemetry-sdk/index.js"; import axios from "axios"; +import { getFontFamily } from "./utils/fontUtils"; +import { getLocalData } from "./utils/constants"; const App = () => { const navigate = useNavigate(); const ranonce = useRef(false); + // Update CSS variable --theme-font based on language + useEffect(() => { + const updateThemeFont = () => { + const lang = getLocalData("lang"); + const fontFamily = getFontFamily(lang); + document.documentElement.style.setProperty("--theme-font", fontFamily); + }; + + // Update on mount + updateThemeFont(); + + // Listen for language changes in localStorage + const handleStorageChange = (e) => { + if (e.key === "lang") { + updateThemeFont(); + } + }; + + // Listen for storage events (when language changes in other tabs/windows) + window.addEventListener("storage", handleStorageChange); + + // Also check periodically for language changes (for same-tab changes) + const interval = setInterval(() => { + updateThemeFont(); + }, 1000); + + return () => { + window.removeEventListener("storage", handleStorageChange); + clearInterval(interval); + }; + }, []); + useEffect(() => { const handleBeforeUnload = (event) => { window.telemetry && diff --git a/src/RFlow/Barakhadi.jsx b/src/RFlow/Barakhadi.jsx index 58f224b8..b24f9e65 100644 --- a/src/RFlow/Barakhadi.jsx +++ b/src/RFlow/Barakhadi.jsx @@ -21,6 +21,7 @@ import { setLocalData, sendTestRigScore, } from "../utils/constants"; +import { getFontFamily } from "../utils/fontUtils"; import { useNavigate } from "react-router-dom"; import { response } from "../services/telementryService"; import { Typography, Stack, IconButton } from "@mui/material"; @@ -59,6 +60,7 @@ import { fetchPaginatedContent, } from "../services/content/contentService"; import { updateLearnerProfile } from "../services/learnerAi/learnerAiService"; +import { splitGraphemes } from "split-graphemes"; const theme = createTheme(); @@ -1496,6 +1498,23 @@ const barakhadiCharts = { "హం", "హః", ], + ళ: [ + "ళ", + "ళా", + "ళి", + "ళీ", + "ళు", + "ళూ", + "ళృ", + "ళె", + "ళే", + "ళై", + "ళొ", + "ళో", + "ళౌ", + "ళం", + "ళః", + ], }, kn: { @@ -2060,221 +2079,1959 @@ const barakhadiCharts = { "ಹಂ", "ಹಃ", ], + ಳ: [ + "ಳ", + "ಳಾ", + "ಳಿ", + "ಳೀ", + "ಳು", + "ಳೂ", + "ಳೃ", + "ಳೆ", + "ಳೇ", + "ಳೈ", + "ಳೊ", + "ಳೋ", + "ಳೌ", + "ಳಂ", + "ಳಃ", + ], }, }; -const wordData = { +export const wordData = { hi: [ { - text: "भारत", - audio: "e0babcda-d6ff-4fed-a36d-5ccdd831b1f2.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.bharataudio), + text: "कार", + audio: "ed1a6a41-893f-49e6-9e9f-dba1e89f7480.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कारAudio), + }, + { + text: "रात", + audio: "5a49dd77-a776-4862-a38b-dcff186befe3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रातAudio), + }, + { + text: "पिता", + audio: "004dfced-6aa6-47cb-8369-03e8fe9e5762.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.पिताAudio), + }, + { + text: "किला", + audio: "d8e9b508-93a9-4f27-bece-15935d16fe03.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.किलाAudio), + }, + { + text: "तीर", + audio: "184e3253-8e03-425c-991a-3071943aa704.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.तीरAudio), + }, + { + text: "गीत", + audio: "fd38e29a-123a-4c49-9cce-8b51fd1fcd45.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.गीतAudio), + }, + { + text: "सुख", + audio: "22aa573a-3123-4dba-ba0f-dd285509a8e3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सुखAudio), + }, + { + text: "रुक", + audio: "56ba2119-6d54-403b-b4e9-8ea8d2339917.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रुकAudio), + }, + { + text: "मुख", + audio: "57b73b9b-ab7e-4cc9-be80-24430a7d0124.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मुखAudio), + }, + { + text: "फूल", + audio: "7b1613c9-34c3-4ed8-b365-31fb58ae8ef5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.फूलAudio), + }, + { + text: "रूह", + audio: "1cdaf044-2ef5-4f52-b6e9-0fa0b9393492.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रूहAudio), + }, + { + text: "गृह", + audio: "0d485e95-9904-4236-8624-d8e948380cba.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.गृहAudio), + }, + { + text: "कृपा", + audio: "88dd1023-c26d-41e8-9fae-65138b6752e8.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कृपाAudio), + }, + { + text: "तेल", + audio: "cb29f1ee-ccfe-4d41-836a-ad69272fda1d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.तेलAudio), + }, + { + text: "केला", + audio: "60458bcf-df7e-4f4d-af23-40c1f5563581.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.केलाAudio), + }, + { + text: "बैठ", + audio: "197a75bc-811d-4e6e-8108-f3a2cd3fedbd.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.बैठAudio), + }, + { + text: "कैसे", + audio: "c7138dbb-94aa-49a8-b8fb-d4ca41079b58.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कैसेAudio), + }, + { + text: "शोर", + audio: "086593d8-15d2-4c14-a743-38ac7a382bbe.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.शोरAudio), + }, + { + text: "मोर", + audio: "1f568831-522b-4b82-b4aa-deb7b97f9768.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मोरAudio), + }, + { + text: "कौन", + audio: "8d5b767d-acc0-4916-a75b-19eee1269f27.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कौनAudio), + }, + { + text: "मौज", + audio: "7c1f7f39-b75e-431d-b468-0310c655f2ca.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मौजAudio), + }, + { + text: "हंस", + audio: "4c80a8a6-b55a-45a7-b7f6-6bbf18e05561.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.हंसAudio), + }, + { + text: "नमः", + audio: "dfdc7273-12ae-45e1-81cf-dd3bfe096ff3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.नमःAudio), + }, + { + text: "पानी", + audio: "f676c2e5-b49c-451a-b6fa-42bb5d137030.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.पानीAudio), + }, + { + text: "रुचि", + audio: "07bd5d45-e2fc-442d-b1cc-ff1cd510a710.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रुचिAudio), + }, + { + text: "सामने", + audio: "7bc800b1-5caa-4df5-9a63-20dc77ba3107.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सामनेAudio), + }, + { + text: "शुरू", + audio: "0e11f925-1006-4a7e-ae36-7d103b528b41.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.शुरूAudio), + }, + { + text: "होगा", + audio: "65535afd-3e4f-40cf-98ff-22bd72e6405a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.होगाAudio), + }, + { + text: "दिशा", + audio: "8de794ff-dcb0-483c-991e-f2d09dce8893.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.दिशाAudio), + }, + { + text: "कितने", + audio: "0ca4f842-0f54-4cf5-a649-ec4b3cf05d7f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कितनेAudio), + }, + { + text: "चिड़िया", + audio: "06969b26-1588-4064-aa61-fb8c3a7d665f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.चिड़ियाAudio), + }, + { + text: "सुना", + audio: "5ca9dc01-e318-4201-8a97-bce48ec89d22.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सुनाAudio), + }, + { + text: "रुपया", + audio: "e2b6df09-04e1-4fbc-b363-796e783169b9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रुपयाAudio), + }, + { + text: "कविता", + audio: "21ba9b0b-a4e9-493a-8fa8-72573476724c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कविताAudio), + }, + { + text: "मिला", + audio: "a3c77ef0-6998-4568-ac96-8663d37d8e06.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मिलाAudio), + }, + { + text: "सोमारू", + audio: "f8ae8f6e-7146-4255-92fc-f855715496c9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सोमारूAudio), + }, + { + text: "पहेली", + audio: "760ed308-04f2-4be6-9e04-767fe5b6503d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.पहेलीAudio), + }, + { + text: "कहानी", + audio: "3e8c5933-8d58-46b2-81b6-f146d9dc03b2.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कहानीAudio), + }, + { + text: "तोसिया", + audio: "65f320e0-a177-4278-a4c9-8342c69f64d8.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.तोसियाAudio), + }, + { + text: "बारिश", + audio: "02907e35-3140-4549-bcd5-0440c5e3f091.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.बारिशAudio), + }, + { + text: "कटोरी", + audio: "87751809-a287-48f7-ab21-d9927a27b10e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कटोरीAudio), + }, + { + text: "थाली", + audio: "5e379647-9f49-4c9f-99ce-bb07d13aa5ec.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.थालीAudio), + }, + { + text: "मुझे", + audio: "e0f54b9e-257c-4412-8f18-3da880f84016.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मुझेAudio), + }, + { + text: "तैरना", + audio: "c4a15974-7f17-4cee-99f4-e7d1e8e30aad.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.तैरनाAudio), + }, + { + text: "पिताजी", + audio: "b9fcd2fe-063d-479e-9bee-87bc27f8ee8d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.पिताजीAudio), + }, + { + text: "जाता", + audio: "fadeba57-dbe0-4d24-921a-cc540a9844bd.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.जाताAudio), + }, + { + text: "खाने", + audio: "2098c754-2cef-4103-be9e-6cbdaf0d6775.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.खानेAudio), + }, + { + text: "केतकी", + audio: "38e26d87-2bba-4c79-9b61-ae16fd687286.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.केतकीAudio), + }, + { + text: "सारा", + audio: "be5b0350-762a-47a1-88fd-2d99c0e19ec1.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.साराAudio), + }, + { + text: "नीमा", + audio: "b66da551-e66d-4863-9e31-f2d14f95e165.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.नीमाAudio), + }, + { + text: "लिखना", + audio: "8315af5d-69dc-43b8-880c-fed23bfbbcca.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.लिखनाAudio), + }, + { + text: "लेकिन", + audio: "cb3f8cbe-dd90-4110-8fe2-dbb95888e861.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.लेकिनAudio), + }, + { + text: "देखा", + audio: "057ab9a6-49d4-4c70-877a-b28099266a02.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.देखाAudio), + }, + { + text: "सभी", + audio: "c938d2d8-84fd-4695-bc38-6ca9f05ddf74.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सभीAudio), + }, + { + text: "गाना", + audio: "9e080c4c-0738-4530-ad72-776f84cfe585.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.गानाAudio), + }, + { + text: "मेरे", + audio: "4fd1a368-7e4a-468c-96c0-81e51f3b51c9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मेरेAudio), + }, + { + text: "सवारी", + audio: "353eeb59-e37a-4683-b19f-ef3f01f0f873.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सवारीAudio), + }, + { + text: "दादाजी", + audio: "6f455848-7055-43a5-978a-7d3a022d3921.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.दादाजीAudio), + }, + { + text: "खेलने", + audio: "4d96893c-74fd-45c7-b899-2037873ca9d6.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.खेलनेAudio), + }, + { + text: "भाषा", + audio: "81a25aa1-0e26-4f57-832d-62383fff0c7b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.भाषाAudio), + }, + { + text: "चाचाजी", + audio: "80ae979e-08ef-4d24-8c12-53b4aa953f9d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.चाचाजीAudio), + }, + { + text: "सही", + audio: "f092465e-5b7e-4b7a-a672-c84d67995393.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सहीAudio), + }, + { + text: "बिरंगे", + audio: "1be5f6b1-eb74-4e29-96f2-bcf76bafe212.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.बिरंगेAudio), + }, + { + text: "कुछ", + audio: "20565cd7-0306-42ce-b104-c79773604a95.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.कुछAudio), + }, + { + text: "दुनिया", + audio: "efd155ed-c03e-49ab-8ece-f56a14955e14.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.दुनियाAudio), + }, + { + text: "किसी", + audio: "2c9b8b15-0665-4196-bd62-472488bac265.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.किसीAudio), + }, + { + text: "तुमने", + audio: "106ab882-e79b-4784-87fc-9671a7f20210.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.तुमनेAudio), + }, + { + text: "चाची", + audio: "ba26441e-1fbb-4656-b4fe-ccc0ccb9d7c9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.चाचीAudio), + }, + { + text: "रोटी", + audio: "b07a6dc0-7019-4298-a267-174954bd0568.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रोटीAudio), + }, + { + text: "छोटी", + audio: "dd68999c-c976-4239-8f46-03363901ee00.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.छोटीAudio), + }, + { + text: "खिलौने", + audio: "30d0f48d-b396-4909-9358-7d8d93a4290e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.खिलौनेAudio), + }, + { + text: "गिनो", + audio: "8b2dde34-42c5-4ff1-8385-470bb7fcd061.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.गिनोAudio), + }, + { + text: "लिखो", + audio: "4ceba180-cf06-443f-ac2b-ee035a864340.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.लिखोAudio), + }, + { + text: "देखो", + audio: "199c5bbd-2038-4a18-b376-62190792ea3f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.देखोAudio), + }, + { + text: "जितनी", + audio: "f7dd6e51-c586-45bc-99db-7aff0062e326.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.जितनीAudio), + }, + { + text: "निकला", + audio: "3ded609f-b566-4cb1-b142-df9af9db4cc9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.निकलाAudio), + }, + { + text: "बोला", + audio: "cc9ee737-cb24-4e90-8b40-5e3f04d6ee36.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.बोलाAudio), + }, + { + text: "चूजा", + audio: "8f3a6733-dab1-4306-97eb-27a6a289a630.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.चूजाAudio), + }, + { + text: "घूमने", + audio: "6c24884c-1d68-41cf-bf72-b25677932fff.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.घूमनेAudio), + }, + { + text: "बोले", + audio: "e1657c7d-2dda-4458-9ab5-a65beadcb656.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.बोलेAudio), + }, + { + text: "गिलास", + audio: "aebc36b5-0383-453b-bcba-e717bf86a793.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.गिलासAudio), + }, + { + text: "मिलाना", + audio: "42f84f49-85a0-4775-8853-8a4edc8a9659.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.मिलानाAudio), + }, + { + text: "रूठा", + audio: "fe14271e-ebbc-40f2-9e83-4a2a7dfec66b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रूठाAudio), + }, + { + text: "रुकना", + audio: "08f71060-0783-43e0-aca5-6dede7032337.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रुकनाAudio), + }, + { + text: "रुकाव", + audio: "d79eacc9-99bf-47cf-9de1-3a89f35d1509.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रुकावAudio), + }, + { + text: "रूपा", + audio: "8f4f937e-4efc-4297-a271-4e9877599667.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.रूपाAudio), + }, + { + text: "सिपाही", + audio: "03a37f06-546d-4658-8a56-1e604dae63f1.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.सिपाहीAudio), + }, + { + text: "तितली", + audio: "4f285c9e-17ce-441e-9cc3-0f1bacd8e7be.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.तितलीAudio), + }, + { + text: "हाथी", + audio: "9fedf55c-5913-4aee-a2a4-4573284519bd.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.हाथी2Audio), + }, + { + text: "छाता", + audio: "b9bdb287-48ef-49a7-816a-5ef7029dbbc4.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.छाताAudio), + }, + { + text: "धनिया", + audio: "b796798f-182c-4c47-9489-dc4dca75da10.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.धनियाAudio), + }, + ], + + ta: [ + { + text: "மலை", + audio: "bf0f13d5-f206-4fe2-b0fc-39462362b948.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.மலைAudio), + }, + { + text: "நதி", + audio: "47c2b4ee-88bf-4b4f-92e6-07716978b021.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.நதிAudio), + }, + { + text: "புழு", + audio: "49500432-222b-475c-81c9-d331adfbca3a.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.புழுAudio), + }, + { + text: "வலி", + audio: "5c7cdc08-b216-4317-80e2-a2995aeb1239.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.வலிAudio), + }, + { + text: "தலை", + audio: "e782655f-6da9-4dc8-9f36-ce404c4c53c2.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.தலைAudio), + }, + { + text: "நாடு", + audio: "37a8ced6-8fa4-451d-a712-627c09bd8398.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.நாடுAudio), + }, + { + text: "மாடு", + audio: "37a8ced6-8fa4-451d-a712-627c09bd8398.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.மாடுAudio), + }, + { + text: "மழை", + audio: "0d6e3293-7cd1-40ac-a971-46062a2c5bda.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.மழைAudio), + }, + { + text: "கடை", + audio: "81bf37f3-4517-4af1-94d6-d8f801e20534.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.கடைAudio), + }, + { + text: "வீடு", + audio: "7837a882-d5c5-40b6-b4d2-3d72d42427c7.mp3", + segmentedAudio: getAssetAudioUrl(s3Assets.வீடுAudio), + }, + ], + + te: [ + { + text: "గది", + audio: "99be1000-4455-456b-aec4-7bb64eb03357.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గదిAudio), + image: "a412d0ae-c80b-42b2-aa37-d0d01cd35478.png", + }, + { + text: "చేను", + audio: "5cb22860-042d-4faa-972d-d9f5c51f9616.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చేనుAudio), + image: "6d77c3e7-096d-404d-87ad-a374bcbc5274.png", + }, + { + text: "చీర", + audio: "b3fd1e21-d332-4c24-b57b-875343e509cc.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చీరAudio), + image: "fc0b7594-ea85-4c4b-bd23-724b1fff54ec.png", + }, + { + text: "గెల", + audio: "12fc97c2-ed14-4ed5-b3e0-5830e083977b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గెలAudio), + image: "de4b43c5-67a0-4689-b39c-7c8b3f45d0f3.png", + }, + { + text: "సౌధం", + audio: "b8238760-b23f-48a7-836b-5d9a383b42d2.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.సౌధంAudio), + image: "d35e6253-d1a9-431c-acd0-b28acbb708f8.png", + }, + { + text: "గృహం", + audio: "152bb7b3-89e5-4d38-a57e-a389ebf62571.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గృహంAudio), + image: "91234a7f-da48-41a0-a792-0393882716bf.png", + }, + { + text: "జాతర", + audio: "09412f04-c180-4411-bab4-c1ba07eef7c4.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జాతరAudio), + image: "11d8d43d-2906-42cf-aac2-e2e9cd0f77d7.png", + }, + { + text: "గీరు", + audio: "76a165f2-960e-4445-9c60-89ac34197a2b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గీరుAudio), + image: "1734e535-62d4-4c73-8fb5-9a3b6d7c6247.png", + }, + { + text: "జైలు", + audio: "84ac6622-8ec6-4634-94f7-2a6fd1cc2dcf.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జైలుAudio), + image: "833f3cf5-0a7a-44c2-86a0-5c5ad68ba01d.png", + }, + { + text: "కోకిల", + audio: "adfb12ce-cc02-447d-bea6-c75455fa4eb3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.కోకిలAudio), + image: "3e136472-4fc9-4acc-9582-5f7118c9f983.png", + }, + { + text: "నౌక", + audio: "99db1186-5a62-4ae2-9987-1b10e5288107.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నౌకAudio), + image: "e624459d-ec03-4d3c-9138-6826c1561b5b.png", + }, + { + text: "నీకు", + audio: "d767350b-192c-46ab-8794-c23936999100.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నీకుAudio), + image: "b94ad383-8a3b-4b0d-972c-4028a5fa0d9a.png", + }, + { + text: "పోరు", + audio: "78868085-2c81-4c3b-ad59-79efa6d8d4b0.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పోరుAudio), + image: "ee3484f6-b1cd-4deb-884d-51c3bbb89f2c.png", + }, + { + text: "మెడ", + audio: "87961184-a658-4be4-912f-6b6209535e00.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మెడAudio), + image: "4bbc1ce5-b04a-4ffc-8cd4-81e4688e4fe3.png", + }, + { + text: "టోపీ", + audio: "cfa1ccfd-7d25-409d-a5f9-95479d761229.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.టోపీAudio), + image: "e2369a79-5712-4360-9a29-15a4bdd78ab4.png", + }, + { + text: "డైరీ", + audio: "bba5dbcf-9587-4a06-9fe2-999aed9e32c5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.డైరీAudio), + image: "f42a9d60-2521-482d-80b5-c1c53d0ee7e0.png", + }, + { + text: "రోకలి", + audio: "0ce83c53-9c18-407f-804f-223dfa032061.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.రోకలిAudio), + image: "cc334694-4d70-41a2-8e25-f727d0d62a67.png", + }, + { + text: "సూది", + audio: "73e2d863-5038-41ff-95bf-98529ac03fa8.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.సూదిAudio), + image: "4d2a8a6a-f875-4a90-aee4-43f78a8ea152.png", + }, + { + text: "జౌళి", + audio: "a058121e-cf67-487c-8c87-9ce17e85ab78.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జౌళిAudio), + image: "51cfde8d-121c-422f-a114-5d1e16103a0f.png", + }, + { + text: "సీసా", + audio: "e4f2e7f1-0089-4364-9073-277f18bcb06f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.సీసాAudio), + image: "f19063f9-be03-499e-bbe6-515242967a59.png", + }, + { + text: "బంతి", + audio: "0fc8d70e-4c08-4404-985e-607d2ddd4d5c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.బంతిAudio), + image: "7df79593-184f-4cd0-9afe-cea8cf089b8b.png", + }, + { + text: "టౌను", + audio: "e1f8bfe3-bbad-4f8b-ae88-55fe53364e2e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.టౌనుAudio), + image: "3cd85cd7-2b7a-454b-8f35-779f22fd394a.png", + }, + { + text: "బాలిక", + audio: "5a661a20-5587-4a05-8b04-7bb80c78af7f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.బాలికAudio), + image: "5a305c06-d14e-4d24-adae-f7186211f62b.png", + }, + { + text: "నెమలి", + audio: "ba2d7442-074d-454e-9c24-2780eb6f2e59.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నెమలిsplitAudio), + image: "dca125fa-2293-4f78-ac7d-f89879d99bc8.png", + }, + { + text: "పశువు", + audio: "b19ed5da-a06f-4d28-8736-b56ccf2b81ea.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పశువుAudio), + image: "890c0bd7-4b36-4302-9926-22de387e7c91.png", + }, + { + text: "బూడిద", + audio: "222cf053-ce7d-43e6-9e5d-0e573a110b31.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.బూడిదAudio), + image: "cca211fe-c2c7-40a8-8786-4cbee2db69df.png", + }, + { + text: "నుదురు", + audio: "3e27d58d-72c3-4cc3-8886-e0444293f576.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నుదురుAudio), + image: "e88113f6-dfce-412c-9a74-0fbbd9197381.png", + }, + { + text: "మైదానం", + audio: "62d51ed5-c606-435e-a23d-cee6cbd5446d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మైదానంAudio), + image: "15d25aa8-be33-45f8-99a4-4a82dfd87583.png", + }, + { + text: "నొసలు", + audio: "b94dff85-d1c9-474c-a067-b458c7560347.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నొసలుAudio), + image: "c556a6b5-487a-4917-b2d8-abf8293175b1.png", + }, + { + text: "పొదుపు", + audio: "f714b74a-f9ba-4bf3-a09a-0a984a7696c6.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పొదుపుAudio), + image: "e54e4cef-714c-41dc-8f73-80561416ff94.png", + }, + { + text: "జెండా", + audio: "f5129861-6aa7-4849-988d-dfa1831e3a75.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జెండాAudio), + image: "a16f477e-182b-4688-8a14-aea49be252c5.png", + }, + { + text: "పెరుగు", + audio: "ae7f1913-1652-4979-8323-7a1c9fa83c56.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పెరుగుAudio), + image: "e9435fac-5d6b-4769-b3af-afd1de59a36e.png", + }, + { + text: "భూకంపం", + audio: "0bf66c03-37ba-49c8-86bf-7dad77bf4395.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.భూకంపంAudio), + image: "6d3f703e-e1ed-44f3-83ee-51ec68b72d05.png", + }, + { + text: "పావురం", + audio: "e346c5ad-7bb6-4b64-81bd-359ac7ef566f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పావురంAudio), + image: "710a4d93-ddcd-4487-a829-c2eb9017ca38.png", + }, + { + text: "జూకాలు", + audio: "c6f92ef1-874e-48c6-9417-b58b3c7ac707.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జూకాలుAudio), + image: "4f2c3ffd-8d5d-4a91-a8fb-16345b040ee2.png", + }, + { + text: "పసిడి", + audio: "f1838fba-64c7-4768-a9e7-7750a250a4c6.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పసిడిAudio), + image: "a0c79728-e6ea-42ad-9a7b-6c37eaba02f1.png", + }, + { + text: "గాజులు", + audio: "2d938405-8a58-4650-b174-d15988d930e5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గాజులుAudio), + image: "5515f309-6a1c-407c-b598-64745ca1df32.png", + }, + { + text: "నృపతి", + audio: "35db88d9-bd7f-4d6c-b966-fd32f9a2e791.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నృపతిAudio), + image: "4024439e-aaf3-4334-8f4c-18a73f06e535.png", + }, + { + text: "పైసలు", + audio: "1f15e531-f83f-440b-b037-b7ec15ab2ba3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పైసలుAudio), + image: "62f77432-0424-4996-8c96-32c9c235027e.png", + }, + { + text: "మొదలు", + audio: "06b635ed-ee50-4979-bf3d-6120053e423c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మొదలుAudio), + image: "673c3eb9-d7d2-41bd-a493-dc76d745d7e7.png", + }, + { + text: "మాటలు", + audio: "7ff704dc-f1f0-48d0-8ab3-6fa3de08c68e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మాటలుAudio), + image: "d9e87544-c93f-4c00-a6f9-cfe951fa7528.png", + }, + { + text: "వివాహం", + audio: "80f926b8-3fa2-4d18-838a-9ef3d6b04b45.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.వివాహంAudio), + image: "8ddc1a76-9d67-4d1e-a13f-f1d4b8cd0842.png", + }, + { + text: "మూకుడు", + audio: "ea8f4894-3848-44a9-98d0-b133b76942ce.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మూకుడుAudio), + image: "051f14a1-9841-4f86-909d-e0b4406868b1.png", + }, + { + text: "హృదయం", + audio: "20d376c1-453f-4b9b-b573-08016932afde.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.హృదయంAudio), + image: "6b1569b0-f4dc-44c4-ac06-618ff0737979.png", + }, + { + text: "బేడీలు", + audio: "be6164c1-4529-4862-8586-a714b2b88652.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.బేడీలుAudio), + image: "d21c747d-17cf-45ff-84c2-1107dd1a1f6b.png", + }, + { + text: "శైశవం", + audio: "aec28d63-9afe-4b42-bd37-5ceef3255def.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.శైశవంAudio), + image: "aba49865-3b6b-4a4f-a55b-a0ad217c2486.png", + }, + { + text: "గొడుగు", + audio: "dc4638d0-11b6-4270-b9a2-0056fea38ad5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గొడుగుAudio), + image: "6af4fdbf-f682-4ea4-8892-3c604254b8a4.png", + }, + { + text: "పొడవైన", + audio: "4a9a9fe9-643a-4a95-bd60-ffa1d5cd2981.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పొడవైనAudio), + image: "cb45acc9-82b5-4229-bac6-7d3f5b38ba09.png", + }, + { + text: "సొరకాయ", + audio: "336d1c4f-0858-4afb-811b-121576a5cc97.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.సొరకాయAudio), + image: "06288e6d-5de0-48c0-93f7-62396c781901.png", + }, + { + text: "జలపాతం", + audio: "4ea80f33-49fc-44bb-b16a-65db31c46495.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జలపాతంAudio), + image: "d3d85f0b-0b28-4794-b6a3-a93b19ec933b.png", + }, + { + text: "గిజిగాడు", + audio: "b9bf7602-1ac2-4101-b45e-4ee2c7d0f99e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గిజిగాడుAudio), + image: "07b0c519-e7cb-4672-8cda-1fd21543b813.png", + }, + { + text: "ఊరేగింపు", + audio: "2c66e48d-3b6a-4df5-842a-b95a041a3741.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ఊరేగింపుAudio), + image: "7d1e1579-63c6-4181-8d8b-d3f357dda882.png", + }, + { + text: "చూడలేదు", + audio: "41640d7e-a923-4cc2-a174-072f27000c31.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చూడలేదుAudio), + image: "0456a5d0-e645-4bb7-839a-0c0e01d05ec5.png", + }, + { + text: "గులాబీలు", + audio: "74b1483a-2701-4608-9f5c-572fa8e3bbe2.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గులాబీలుAudio), + image: "1d9a6c6b-7879-41e8-8d1a-40631dda2891.png", + }, + { + text: "మీగడ", + audio: "c669e1a0-18c7-4531-ad23-8dca74ad2803.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మీగడAudio), + image: "0b46d98e-dbdd-485d-8472-51e93fd73d47.png", + }, + { + text: "చీపురు", + audio: "8277b035-401b-488d-a113-8f9cf226c118.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చీపురుAudio), + image: "80bed291-3043-46ae-8b4a-da909d464147.png", + }, + { + text: "రెండు", + audio: "5679c906-2d8c-4ff7-9c6a-2861f6cbe1a7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.రెండుAudio), + image: "5a576136-cc9c-4c95-80b1-f2de07a6194d.png", + }, + { + text: "మేఘం", + audio: "ab6251b6-aff3-475f-af7f-841e8068b978.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మేఘంAudio), + image: "d59582fd-2d76-4498-81f6-73e0c963ffda.png", + }, + { + text: "నేల", + audio: "07510b3c-1efd-4cc9-9a0d-7cf68640fd5e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నేలAudio), + image: "3dc59fdc-08e0-4d82-bbb4-404759377fc7.png", + }, + { + text: "తోలు", + audio: "a661db28-f049-4268-99a2-6cbf10ba23c0.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.తోలుAudio), + image: "53a752bd-4f2b-431a-abad-b16c3adf4878.png", + }, + { + text: "పూజారి", + audio: "aec1ba7e-4fe4-4ee0-8579-8ef868b10630.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పూజారిAudio), + }, + { + text: "జాలరి", + audio: "28b14c01-2219-4f36-b7c9-97312bae3e8a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జాలరిAudio), + }, + { + text: "కూజా", + audio: "8089314a-507c-481f-9864-b3cf1e85c9d4.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.కూజాAudio), + }, + { + text: "దివిటి", + audio: "3a63307c-7927-4a42-af7f-eb80653f1a67.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.దివిటిAudio), + }, + { + text: "గాలి", + audio: "99bf2848-634c-4fe8-846d-8db895137461.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గాలిAudio), + }, + { + text: "గోడ", + audio: "887c77b8-e4c3-4b1a-925a-d1f7f2801797.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గోడAudio), + }, + { + text: "దురద", + audio: "4d1acab5-5e40-407d-a577-01bea8a05203.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.దురదAudio), + }, + { + text: "చదువు", + audio: "104ee587-c591-494d-91da-2973160a72e4.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చదువుAudio), + }, + { + text: "చేదు", + audio: "aa994426-43e8-42b4-a20e-605bf8eff8e2.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చేదుAudio), + }, + { + text: "కూతురు", + audio: "926741d2-4784-4613-89d2-8c847c8c7af4.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.కూతురుAudio), + }, + { + text: "పెసలు", + audio: "8f301de3-f6c5-444f-93d7-d6741a24031d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పెసలుAudio), + }, + { + text: "హారం", + audio: "cb7d1ec9-d125-4675-99ed-795e40b598c5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.హారంAudio), + }, + { + text: "కిటికి", + audio: "ee52f2c6-cbd4-49d2-b11c-dd4b619558cb.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.కిటికిAudio), + }, + { + text: "చీకటి", + audio: "b8a3ac68-8037-4947-b831-6505c13f4c7e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చీకటిAudio), + }, + { + text: "శునకం", + audio: "b6ccd267-a185-47b9-89de-77e2a7633b30.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.శునకంAudio), + }, + { + text: "గెలుపు", + audio: "432b6c3b-69ac-4910-9b07-6f8f028cc60e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గెలుపుAudio), + }, + { + text: "మేక", + audio: "1daa13cb-4556-4f39-adf7-4f6d603ac1bd.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మేకAudio), + }, + { + text: "కోడి", + audio: "78f5e8cf-ce12-4ece-8f66-7cb3f127185a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.కోడిAudio), + }, + { + text: "పాలు", + audio: "6f4af884-14b5-499a-be8d-98f7360a05ee.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పాలు2Audio), + }, + { + text: "నీడ", + audio: "7adf2896-aaa5-4c0f-b7d4-708c9549ea5d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.నీడAudio), + }, + { + text: "పూవులు", + audio: "56674e51-b2d2-4878-a538-6ee62b15d58d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పూవులుAudio), + }, + { + text: "గురువు", + audio: "94fda31e-3e16-4274-9c9f-d206a89be48d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గురువుAudio), + }, + { + text: "జూలు", + audio: "6864cfd4-45e0-48be-aa20-055a31b2e8c7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జూలుAudio), + }, + { + text: "దుకాణం", + audio: "96633501-7e6d-4c84-a4e2-fac2e2d7ca3d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.దుకాణంAudio), + }, + { + text: "పీట", + audio: "afe1b8de-1d0a-45f9-980b-4e3bc35514a9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.పీటAudio), + }, + { + text: "సాగరం", + audio: "e51544df-0016-4f94-9411-2344d73b8e9c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.సాగరంAudio), + }, + { + text: "మీసాలు", + audio: "936658cf-daa7-4e97-8098-b5bea062feec.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మీసాలుAudio), + }, + { + text: "తిలకం", + audio: "89762672-d90b-463e-9bcd-5cda1da47deb.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.తిలకంAudio), + }, + { + text: "చామంతి", + audio: "caa3a4fb-726d-4e07-9e77-707696d8b1ad.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చామంతిAudio), + }, + { + text: "గిరి", + audio: "808470e2-db4f-4dfe-b15c-1e565c6eb29f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.గిరిAudio), + }, + { + text: "తోక", + audio: "f4c70a34-054c-4617-acf6-13fd7a9b9224.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.తోకAudio), + }, + { + text: "మామిడి", + audio: "d88e6f1f-06ee-448f-8489-9c305cf60d22.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మామిడిAudio), + }, + { + text: "మీనం", + audio: "946144d3-d395-49ab-85b3-8e0ca89a676e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మీనంAudio), + }, + { + text: "మూతి", + audio: "e341db0c-b86e-452c-bced-fb2d170c7da7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.మూతిAudio), + }, + { + text: "రోజా", + audio: "f687bf5f-d27a-44e7-80ea-d979f37023e3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.రోజాAudio), + }, + { + text: "బజారు", + audio: "bd7ede7f-63b0-4f52-9416-10002d738733.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.బజారుAudio), + }, + { + text: "రూపాయి", + audio: "91523dff-d547-4490-8a5b-bfa00cd3fc09.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.రూపాయిAudio), + }, + { + text: "చపాతీ", + audio: "1df86b9a-ec60-44a6-befd-9de0e9f6e467.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.చపాతీAudio), + }, + { + text: "జంతువు", + audio: "2e5128f6-a445-4ddf-a03c-fb8f2bd16c77.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.జంతువుAudio), + }, + { + text: "వేపాకు", + audio: "6ce13b8d-fb3f-413a-bf3f-79df9cf2ae81.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.వేపాకుAudio), + }, + ], + + kn: [ + { + text: "ಕಾಗೆ", + audio: "640f5b57-8228-42f8-adde-315dfc18b131.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕಾಗೆAudio), + image: "e253a336-c072-4c06-91ce-764e5bd688b3.png", + }, + { + text: "ಮಾಲೆ", + audio: "99dce746-98fe-4884-8466-9ae04e3b01bb.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮಾಲೆAudio), + image: "28e07d8f-bbe1-42ee-9907-4fe4b3fe8eb8.png", + }, + { + text: "ಗಿಳಿ", + audio: "a5d97e00-305b-4bb5-a411-59521c5cc59e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗಿಳಿAudio), + image: "1c66cc89-cee5-4ae4-b065-9c4f897351a0.png", + }, + { + text: "ಸಿರಿ", + audio: "3b64139f-06fb-4f5d-9692-434a632c13af.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸಿರಿAudio), + }, + { + text: "ಗುರು", + audio: "4f81d178-39a5-4db3-bf6d-2be5b827ab95.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗುರುAudio), + image: "b35db194-aec3-4637-b18d-0dfcbf2bfeb4.png", + }, + { + text: "ಹುಲಿ", + audio: "3e2eae4a-c015-4a8a-b9e9-153933b20180.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹುಲಿAudio), + image: "d6bfebb5-6731-40c3-b577-57eeed620104.png", + }, + { + text: "ಮೂರು", + audio: "fb07e346-2dc9-4620-b839-02235827f0e0.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೂರುAudio), + image: "c4314928-d735-494e-b476-b21b628aeeae.png", + }, + { + text: "ಗೂಡು", + audio: "a639c3f3-f94c-45be-b09a-50cf211d8f0f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗೂಡುAudio), + image: "0a343fec-8a96-42fa-9a6d-0b41cbf3f2c3.png", + }, + { + text: "ನೃಪ", + audio: "8d335489-a84c-4f10-81d8-94e64595d2c1.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನೃಪAudio), + }, + { + text: "ಕೃತಿ", + audio: "bb88b0d2-3d70-4b47-9d5f-a30a90c6a4b4.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೃತಿAudio), + image: "46580622-a58e-431b-9183-d2d2528fb8c1.png", + }, + { + text: "ಕೆರೆ", + audio: "80e0b2c1-9e0d-4690-806f-023cefcd1714.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೆರೆAudio), + image: "76579bd6-8a3c-4349-9396-f14678374c97.png", + }, + { + text: "ಮೆರೆ", + audio: "77a87356-f84e-4703-ba13-bb38fcdbd20a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೆರೆAudio), + image: "93cf4b16-18c6-4b7f-8f48-8407a0f8692b.png", + }, + { + text: "ದೇವಿ", + audio: "d3830e93-7aed-4679-bd07-dc2d00eb3435.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ದೇವಿAudio), + image: "4e7b5fde-585c-49ca-9e46-4897a39f3762.png", + }, + { + text: "ವೇಷ", + audio: "20482681-fda8-4332-b807-7991e72b732a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ವೇಷAudio), + image: "f76d3bc6-6e04-4de3-8e53-2a8460385701.png", + }, + { + text: "ಕೈದಿ", + audio: "003c8ba2-5e95-4867-b585-8112d3e76cfe.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೈದಿAudio), + image: "68ef05f4-86c1-482f-9193-125ed968ec08.png", + }, + { + text: "ಪೈರು", + audio: "db39a2c8-c662-47ec-99a7-18b323140152.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಪೈರುAudio), + image: "e58ee2c5-1862-4ad9-b2b0-a8c51dcaf6fc.png", + }, + { + text: "ಹೊರೆ", + audio: "a60f497e-0f5d-4949-b5f8-0ff8cc8baece.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹೊರೆAudio), + image: "dd73e654-4008-436b-aeed-682b3d665324.png", + }, + { + text: "ಕೊಳೆ", + audio: "786e40da-51eb-4087-90c1-18de17fc64af.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೊಳೆAudio), + image: "f8d81fef-871a-47ef-aa27-1f04b3be1e42.png", + }, + { + text: "ಹೋರಿ", + audio: "74861ae4-7866-44df-b598-d9858b5aa109.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹೋರಿAudio), + image: "b666a936-bfe3-4173-92a4-55d4c86d5e06.png", + }, + { + text: "ನೋಟು", + audio: "f5c6af01-acc7-43fa-963c-923a23c5d195.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನೋಟುAudio), + image: "2241d366-02f6-4cfb-9876-90b233c2a79e.png", + }, + { + text: "ದೌಡು", + audio: "75370ff7-f511-4b30-b04f-3b627b38140d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ದೌಡುAudio), + image: "a832da6c-8fe8-4384-91f3-cd5eaf57e59e.png", + }, + { + text: "ಜೌಗು", + audio: "12ad62f1-8387-4783-bf77-5b7b451d9409.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಜೌಗುAudio), + image: "f7e767ca-7d1c-4eb5-95d2-ce9a8ca76493.png", + }, + { + text: "ಕಂಸ", + audio: "b1c59fd6-caf5-4649-bddc-587ce20e7a2b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕಂಸAudio), + image: "5b444c61-2774-4afd-83dd-4510ec177c53.png", + }, + { + text: "ನಂದಿ", + audio: "b7220ae0-cb33-4294-b532-062876eb4b7e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನಂದಿAudio), + image: "6554e275-2e00-4dcc-9b83-132f8ecb71d4.png", + }, + { + text: "ಹಾವು", + audio: "583d7560-0eb1-481c-9497-cb9069dfd357.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹಾವುAudio), + image: "03531c43-0a46-49e4-ab85-b0ef3d943817.png", + }, + { + text: "ಬಿಳಿ", + audio: "e6015919-6993-4fae-b5ab-88dda8b3c0b6.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಬಿಳಿAudio), + image: "e5113c99-d0f3-4a2e-bfd5-a629220bd259.png", + }, + { + text: "ಹೊಗೆ", + audio: "9b7a9d58-a3a8-4261-a12a-d3e95deda693.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹೊಗೆAudio), + image: "6cb23acd-f1cb-40ea-b30a-d19be2198c4d.png", + }, + { + text: "ಕೆನೆ", + audio: "18494ea5-d06d-449b-b554-42d1838c834d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೆನೆAudio), + image: "cbdc6d69-c2eb-4add-a1e0-a2078e032c1b.png", + }, + { + text: "ಕೇಳು", + audio: "8d906ef5-8665-423f-8a41-88844ab5bf3d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೇಳುAudio), + image: "abd0a043-baf0-4a5f-ab3e-3b65ac659b72.png", + }, + { + text: "ನೌಕೆ", + audio: "8b5dc5e8-d5e7-4156-9aa9-f4aba931d8cc.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನೌಕೆAudio), + image: "b719896c-d994-4d0e-80eb-d8681ff2cc6e.png", + }, + { + text: "ನಾಳೆ", + audio: "306fd589-aa0c-47c7-a141-7d0110dd3d2c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನಾಳೆAudio), + image: "ea171f42-8eac-471a-b991-c1ffb136576f.png", + }, + { + text: "ಚೀಟಿ", + audio: "c273bae5-641e-4c68-a79e-737f78cfa187.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಚೀಟಿAudio), + image: "105945aa-0d2a-4f20-938b-2cb132f714e6.png", + }, + { + text: "ಠೀವಿ", + audio: "9b37c83c-046c-46a1-9700-4ac52f14b152.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಠೀವಿAudio), + image: "99ff1b88-0434-46f0-80cd-56824e50e9df.png", + }, + { + text: "ಸುಲಿ", + audio: "1939ff11-8138-4dcd-9590-87538f0d4533.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸುಲಿAudio), + image: "e8de0e66-87d1-4adf-ab0b-68a41b1a4d56.png", + }, + { + text: "ಬೆವರು", + audio: "c6b057ca-9d39-43d7-a6f4-043e42687849.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಬೆವರುAudio), + image: "4b404cd8-bad2-42ba-96fa-724106f56e0c.png", + }, + { + text: "ಹೇರು", + audio: "de658972-f471-4a33-946a-41150b66a020.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹೇರುAudio), + image: "92ab3f00-fb55-4907-9319-b33fb2685d8e.png", + }, + { + text: "ಹಾಡು", + audio: "e1a93d65-2692-4e72-961c-a3e07897063d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹಾಡುAudio), + image: "e54a370c-a7c3-4ec6-b035-b383c24f32ee.png", }, { - text: "राजा", - audio: "b4edcfa0-91cf-4343-91f9-35fd8c691fcf.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.rajaaudio), + text: "ತೊರೆ", + audio: "34bf58ce-6b2d-490d-8c59-81e27e242344.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ತೊರೆAudio), + image: "bf6db23c-f0a7-4f36-9a06-62c1dd410590.png", }, { - text: "जल", - audio: "551fee7a-fad6-4c0b-a384-5cee9aa7c7c2.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.jalaudio), + text: "ಚೇಳು", + audio: "669e74d0-3062-44ee-9870-f895cf2b9244.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಚೇಳುAudio), + image: "f8e90a67-d8e9-493d-8d00-7e33cfe84f3d.png", }, { - text: "भालू", - audio: "18cf4ec8-4669-49eb-81dc-622196bd226a.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.bhaluaudio), + text: "ಗೇಟು", + audio: "6a92fef1-f3f6-41e3-8384-847f91458fe9.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗೇಟುAudio), + image: "2bdd8908-0b3c-422e-becc-0b944b3c8da3.png", }, { - text: "किताब", - audio: "42dfd842-8e09-4ab1-b14a-5b2afab33b5c.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.kitabaudio), + text: "ಜೈಲು", + audio: "9c5137c7-e192-43b3-b4c8-f7443700efab.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಜೈಲುAudio), + image: "05a2bde9-53db-4748-b265-8029272e189a.png", }, { - text: "नदी", - audio: "07346231-fd5b-4a41-82e8-c1f0be6a7a85.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.nadiaudio), + text: "ಬಾಯಿ", + audio: "e9625e8e-5108-4c03-a41c-f1a18c3bed78.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಬಾಯಿAudio), + image: "014cd9da-eace-4622-a808-cde08cb14ecb.png", }, { - text: "केला", - audio: "1b1b2c77-88e1-46e0-b8a9-f94561d34d4a.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.kelaaudio), + text: "ಮೊರೆ", + audio: "bc477f33-447a-4100-8be4-347dcdb1f170.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೊರೆAudio), + image: "0cb43293-5634-4fe4-a3a2-1914d1e7bc42.png", }, { - text: "पपीता", - audio: "7ffaaae5-31ff-413c-ae1f-eb51780cf4d3.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.papitaaudio), + text: "ಡಾಬಾ", + audio: "e17983d7-75a6-4efb-a31a-a291c6f0d28d.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಡಾಬಾAudio), + image: "4cc4156b-12cc-41c6-a2ac-96e1a55babe5.png", }, { - text: "पहाड", - audio: "97cd336e-0495-4aaa-8f64-6764f2714f6f.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.pahadaudio), + text: "ಗೋವು", + audio: "dd891c81-eb46-4c87-895a-d206c5e6f162.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗೋವುAudio), + image: "52a0bf00-0577-4024-aec1-a493428f9584.png", }, { - text: "सेब", - audio: "1ce9cb46-6761-4c97-b184-ed123ea49de5.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.sebaudio), + text: "ಕುದಿಸು", + audio: "fcd85161-a975-4150-99e3-d109efaa51fc.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕುದಿಸುAudio), + image: "70f2f7a1-0251-4fc2-99c3-2463d3c69625.png", }, - ], - - ta: [ { - text: "மலை", - audio: "bf0f13d5-f206-4fe2-b0fc-39462362b948.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.மலைAudio), + text: "ಡೋಲು", + audio: "bddf7f73-4045-47ff-97b7-8c5f923e7e07.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಡೋಲುAudio), + image: "b4261f0c-4a6a-447c-9a86-552afc73bdd7.png", }, { - text: "நதி", - audio: "47c2b4ee-88bf-4b4f-92e6-07716978b021.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.நதிAudio), + text: "ಪೌಳಿ", + audio: "5aeed7b3-b676-40f7-a2fb-183b263cf9c7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಪೌಳಿAudio), + image: "50f61d8d-78a5-4bac-b8a5-0deb4fc2cae7.png", }, { - text: "புழு", - audio: "49500432-222b-475c-81c9-d331adfbca3a.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.புழுAudio), + text: "ಮೋರಿ", + audio: "9d6c2910-9192-48e7-9e20-1f8819576da2.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೋರಿAudio), + image: "4ca102cf-ea65-4a84-b2b9-8fec60493131.png", }, { - text: "வலி", - audio: "5c7cdc08-b216-4317-80e2-a2995aeb1239.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.வலிAudio), + text: "ನೀರು", + audio: "7a235fbe-b81b-4c90-83d9-902fa8072868.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನೀರುAudio), + image: "425cc296-48c8-4c6b-ac30-de0f32751cfd.png", }, { - text: "தலை", - audio: "e782655f-6da9-4dc8-9f36-ce404c4c53c2.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.தலைAudio), + text: "ದೋಣಿ", + audio: "32444ad8-33c3-4456-b1ab-5d11441c813e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ದೋಣಿAudio), + image: "993dd0c4-fff6-4542-b1d0-a836adbdbec4.png", }, { - text: "நாடு", - audio: "37a8ced6-8fa4-451d-a712-627c09bd8398.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.நாடுAudio), + text: "ದಾರಿ", + audio: "23989e7e-e9ea-4631-b59e-90ca0cd54c03.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ದಾರಿAudio), + image: "a80195ff-96ad-4807-98c8-b4d1bf34e9ca.png", }, { - text: "மாடு", - audio: "37a8ced6-8fa4-451d-a712-627c09bd8398.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.மாடுAudio), + text: "ಸೇರು", + audio: "09d87fb9-a94a-4e1f-9bc4-06384fda1c1f.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸೇರುAudio), }, { - text: "மழை", - audio: "0d6e3293-7cd1-40ac-a971-46062a2c5bda.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.மழைAudio), + text: "ಲೈಲಾ", + audio: "7496ead7-32fa-4f82-996f-2ab5415ec099.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಲೈಲಾAudio), }, { - text: "கடை", - audio: "81bf37f3-4517-4af1-94d6-d8f801e20534.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.கடைAudio), + text: "ವೈರಿ", + audio: "96c9c6ae-5697-4ed8-b875-755b79dbcf2e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ವೈರಿAudio), }, { - text: "வீடு", - audio: "7837a882-d5c5-40b6-b4d2-3d72d42427c7.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.வீடுAudio), + text: "ಕುರಿ", + audio: "9bf872d8-ad12-4a45-b00d-b29aa9585238.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕುರಿAudio), }, - ], - - te: [ { - text: "నీరు", - audio: "b2a623f9-e4d1-427f-a50a-83471aeb8d6e.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.నీరుAudio), + text: "ಗಾಡಿ", + audio: "a3e5b96b-19c9-4f29-a969-f4aafa8b4613.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗಾಡಿAudio), + image: "52235a07-dc63-4744-a1db-c25489a63f3e.png", }, { - text: "పాలు", - audio: "6a5b2232-1afe-49eb-a467-d0d86b1e5daf.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.పాలుAudio), + text: "ಸೌತೆ", + audio: "d708c765-678d-4783-be2f-53f6a549799c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸೌತೆAudio), + image: "28ed196c-1614-4a16-af00-aaf6ee5f67a9.png", }, { - text: "చేప", - audio: "4b9977d3-2b1a-4819-ab81-eae005b93192.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.చేపAudio), + text: "ಸಾರಿಗೆ", + audio: "173765b0-b114-456e-88e1-1d2f6711ccff.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸಾರಿಗೆAudio), }, { - text: "లేడి", - audio: "4204ec95-d07c-452f-884b-8a625ef23bb7.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.పక్షిAudio), + text: "ರಾಣಿ", + audio: "5d1023e2-6e64-41bb-9ca1-8852e3419445.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ರಾಣಿAudio), }, { - text: "నది", - audio: "ab310a2b-a6d4-435a-bcd2-589b33689c23.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.నదిAudio), + text: "ಗೌರಿ", + audio: "3a6ee0b9-fbb3-43bf-b29e-e3c645aa5eaa.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗೌರಿAudio), }, { - text: "కథలు", - audio: "fb3d428a-cd3f-45e5-85a8-162c823cfecb.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.కుక్కAudio), + text: "ವೋಟು", + audio: "ae0a9859-c591-4336-b7e5-e57ffbd9c0d1.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ವೋಟುAudio), }, { - text: "తిను", - audio: "8017f67a-1f3c-4286-82fa-5fb8a290d500.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.పిల్లిAudio), + text: "ತೊಲೆ", + audio: "e1cf8190-8ab8-4d59-beb3-e6d7187a9da3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ತೊಲೆAudio), }, { - text: "మనిషి", - audio: "6dc6f74e-44e2-4c1d-99d0-c08dbd740caf.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.మనిషిAudio), + text: "ಡೌಲು", + audio: "aed19454-f201-4dd3-9fea-90bb24b492b0.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಡೌಲುAudio), }, { - text: "బడి", - audio: "4a3fafe7-b90c-4341-9c38-c40caed08494.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.బడిAudio), + text: "ಮೌನಿ", + audio: "fae27992-6037-4a79-b168-3cbd547da140.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೌನಿAudio), }, { - text: "పని", - audio: "9f7f13c0-95b4-4fec-ae0f-28308d8261a8.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ఇల్లుAudio), + text: "ಸೌದೆ", + audio: "115b8d3f-cfdd-4743-8a43-875b91185a8a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸೌದೆAudio), }, - ], - kn: [ { - text: "ತಂಡ", - audio: "e90023db-551d-462a-a132-dd2d93fd026a.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ನೀರುAudio), + text: "ತೌರು", + audio: "84d65deb-c537-4ab3-9964-365273af469c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ತೌರುAudio), + }, + { + text: "ಕೋಡು", + audio: "2d134b91-528b-41e0-a741-1f1fc85b29eb.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೋಡುAudio), + }, + { + text: "ಗೋಡೆ", + audio: "2bfa5734-2682-4f68-98e5-cdf56ecf620a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗೋಡೆAudio), + image: "793f9411-c33c-4787-87da-d0c9b1b60948.png", + }, + { + text: "ಚೋಟು", + audio: "9e7e572a-a5d6-4b11-befc-a9cf1f920306.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಚೋಟುAudio), + }, + { + text: "ರೋಗಿ", + audio: "33b9a315-15d4-41d8-9127-20032c44914c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ರೋಗಿAudio), + }, + { + text: "ಟೋಪಿ", + audio: "7e4e3651-727b-4c8f-8f31-6430ee7e51d7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಟೋಪಿAudio), + image: "65f987a6-d9bf-49e4-8f1a-ce4af51438a5.png", + }, + { + text: "ತೋಟಿ", + audio: "fe614db1-e930-4f2a-9f83-a60e9b6527f8.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ತೋಟಿAudio), + }, + { + text: "ಕೈದು", + audio: "d16a3bd2-1896-4d28-8da8-90017fc4c100.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೈದುAudio), + }, + { + text: "ಪೈಪು", + audio: "dd9080a5-6cb8-4610-b139-e0ee6dd15eab.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಪೈಪುAudio), + }, + { + text: "ಬೆಳೆ", + audio: "c0b43c86-0c47-4f89-bd04-51551de6f19e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಬೆಳೆAudio), + }, + { + text: "ಸೆಖೆ", + audio: "15bedb48-e5be-462e-8fb5-90586eb0bb29.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸೆಖೆAudio), + }, + { + text: "ಪೇಡೆ", + audio: "f6ff3ce6-b6c9-417d-8f93-b7d239ce3bcc.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಪೇಡೆAudio), }, { - text: "ಹಾಲು", - audio: "c8b3656f-1f13-404e-8409-864fd33c56ac.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಹಾಲುAudio), + text: "ರಂಭೆ", + audio: "59e16e72-638a-4840-8430-689fe413311e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ರಂಭೆAudio), }, { - text: "ಮೀನು", - audio: "18ae34eb-1151-4692-bef4-6c4e9d45c68e.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಮೀನುAudio), + text: "ಗುಡಿಸು", + audio: "7b0a9203-de74-474f-884f-cfb6bba71d6b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗುಡಿಸುAudio), }, { - text: "ಕಮಲ", - audio: "6c751bea-e1d2-440a-8d1c-b8847f59312d.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಹಕ್ಕಿAudio), + text: "ಯುವತಿ", + audio: "06d3a786-fceb-44e7-a751-f750b9577334.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಯುವತಿAudio), + image: "d682a9c4-3f7d-4be8-8ff0-40449136518d.png", }, { - text: "ನದಿ", - audio: "a11f0fe3-431f-4e15-9f85-0e50c6927e1e.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ನದಿAudio), + text: "ಪಾಲಿಸು", + audio: "b311bd3e-bd0b-4e2f-a894-460678ad1146.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಪಾಲಿಸುAudio), }, { - text: "ನಾಯಿ", - audio: "be34bf1d-1a30-4244-a9fc-93872eac28d9.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ನಾಯಿAudio), + text: "ಕಿಟಕಿ", + audio: "a8e3cb6f-a6d4-4991-a459-6babf339508c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕಿಟಕಿAudio), + image: "d642d4c3-f736-4573-ad99-068a7740711b.png", }, { - text: "ಸಿಹಿ", - audio: "cb55bceb-11c9-4eeb-bd4f-28835fdccfdd.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಬೆಕ್ಕುAudio), + text: "ಚಿರತೆ", + audio: "4ce023a0-740a-414b-9905-13d84842eda6.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಚಿರತೆAudio), + image: "840eff6d-3c9a-4b43-83b5-828b7b6d455f.png", }, { - text: "ಮನೆ", - audio: "9d2e0804-5d26-456f-bcae-6d92d39d4091.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಮನೆAudio), + text: "ವಾಸಿಸು", + audio: "29ba902f-8a40-4458-9105-b306c35790a7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ವಾಸಿಸುAudio), }, { - text: "ಪಾಠ", - audio: "26a8679c-cbc5-4a55-84bf-eb87de6678a6.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಪಾಠAudio), + text: "ಹೆರಳು", + audio: "99fbb746-0b52-4075-8a85-90ff6eb84c4b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹೆರಳುAudio), }, { - text: "ತಂಡ", - audio: "ebf6d7ed-84f3-48f7-999b-61ba7ba67e5d.mp3", - segmentedAudio: getAssetAudioUrl(s3Assets.ಬಳ್ಳಿAudio), + text: "ಯಾರಿಗೆ", + audio: "288eb90f-9757-47c2-b643-c5f665a2b922.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಯಾರಿಗೆAudio), + }, + { + text: "ತಾವರೆ", + audio: "79b898b0-45fd-4d59-b45d-4a9175fa18a3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ತಾವರೆAudio), + }, + { + text: "ಜಿನುಗು", + audio: "09c0f7c4-faa8-4583-9ce2-208f10a03a0b.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಜಿನುಗುAudio), + }, + { + text: "ಜೋಗುಳ", + audio: "90768cd0-4fa0-448c-bc63-491f0a35795a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಜೋಗುಳAudio), + }, + { + text: "ತೋರಿಸು", + audio: "aa0fd994-973b-45b5-8b38-2b3f7782d75c.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ತೋರಿಸುAudio), + }, + { + text: "ಯೋಜಿಸು", + audio: "6251dbd1-e721-4b88-bbc0-7ff273d2a156.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಯೋಜಿಸುAudio), + }, + { + text: "ಕೌತುಕ", + audio: "ead4fa16-0876-4cf8-b867-2ddf755783e2.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಕೌತುಕAudio), + }, + { + text: "ಜೊತೆಗೆ", + audio: "413b947f-7cc2-4647-a81d-e2a7c95915d3.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಜೊತೆಗೆAudio), + }, + { + text: "ಬೇಸಿಗೆ", + audio: "c3222f8c-ed2a-4c58-9874-b8e0a4cc6dac.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಬೇಸಿಗೆAudio), + image: "884a48f5-796b-4123-b93a-1496aa2e411b.png", + }, + { + text: "ಟೊಮೆಟೊ", + audio: "237c4721-18e8-44a8-a730-210f4c3621d7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಟೊಮೆಟೊAudio), + }, + { + text: "ಮೈಮರೆ", + audio: "d2283c19-b572-45e3-b1cb-de1612d3b857.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೈಮರೆAudio), + }, + { + text: "ಮೈಸೂರು", + audio: "fd72d9cf-58d7-46cd-b15b-207720b05cf5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮೈಸೂರುAudio), + }, + { + text: "ಯೌವನ", + audio: "86f7e626-51ff-431f-800f-ee3799a831f5.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಯೌವನAudio), + }, + { + text: "ಮುದುಕ", + audio: "e3a96bbc-6065-412e-9eb6-c67a2d620e5e.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಮುದುಕAudio), + image: "5d7c8c79-0aaf-4e9d-a90b-58456a658210.png", + }, + { + text: "ಸೈನಿಕ", + audio: "757c65fe-2aae-4090-b51c-ba2249280042.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಸೈನಿಕAudio), + image: "211f3153-2537-4865-89ff-13c20a091217.png", + }, + { + text: "ನೇರಳೆ", + audio: "7ebb715f-cae4-438f-b414-390e3bf9608a.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ನೇರಳೆAudio), + }, + { + text: "ಹೈದರಾಲಿ", + audio: "22fca0e2-1a3f-44ab-9d96-25b38ef51212.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಹೈದರಾಲಿAudio), + }, + { + text: "ಬೆದರಿಕೆ", + audio: "30b5f29d-6fe8-4658-82c8-8bb0bb219cb7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಬೆದರಿಕೆAudio), + }, + { + text: "ಗೃಹ", + audio: "31b5f29d-6fe8-4658-82c8-8bb0bb219cb7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಗೃಹAudio), + image: "e805c013-eb29-4bf3-abd0-19809c0cb058.png", + }, + { + text: "ಜೇನು", + audio: "32b5f29d-6fe8-4658-82c8-8bb0bb219cb7.wav", + segmentedAudio: getAssetAudioUrl(s3Assets.ಜೇನುAudio), + image: "912c90ab-ea2f-486c-bfb1-84fd8fcca85b.png", }, ], }; + +export const TeluguGunithas = [ + { + audio: "ka.wav", + image: "ka1.png", + }, + { + audio: "Kaa.wav", + image: "kaa2.png", + }, + { + audio: "Ke.wav", + image: "ki1.png", + }, + { + audio: "Kee.wav", + image: "kii2.png", + }, + { + audio: "Ku.wav", + image: "ku1.png", + }, + { + audio: "Koo.wav", + image: "ku2.png", + }, + { + audio: "Kru.wav", + image: "kru.png", + }, + { + audio: "Kroo.wav", + image: "kru2.png", + }, + { + audio: "ke1.wav", + image: "ke1.png", + }, + { + audio: "Kee1.wav", + image: "ke2.png", + }, + { + audio: "Kai.wav", + image: "kai.png", + }, + { + audio: "Ko.wav", + image: "ko1.png", + }, + { + audio: "Koo1.wav", + image: "ko2.png", + }, + { + audio: "Kau.wav", + image: "kau.png", + }, + { + audio: "Kam.wav", + image: "kam.png", + }, + { + audio: "Kaha.wav", + image: "kaha.png", + }, + { + audio: "Ju.wav", + image: "ju.png", + }, + { + audio: "Joo.wav", + image: "joo2.png", + }, + { + audio: "Yaa.wav", + image: "yaa-second.png", + }, + { + audio: "yi.wav", + image: "yi1.png", + }, + { + audio: "yee.wav", + image: "yi-second.png", + }, + { + audio: "Paa.wav", + image: "paa-second.png", + }, + { + audio: "saa.wav", + image: "saa-second.png", + }, + { + audio: "pu.wav", + image: "Pu1.png", + }, + { + audio: "poo.wav", + image: "pu2.png", + }, + { + audio: "vu.wav", + image: "vu1.png", + }, + { + audio: "voo.wav", + image: "vu2.png", + }, + { + audio: "po.wav", + image: "po1.png", + }, + { + audio: "poo1.wav", + image: "po2nd.png", + }, + { + audio: "mo.wav", + image: "mo1.png", + }, + { + audio: "moo.wav", + image: "mo2nd.png", + }, + { + audio: "sau.wav", + image: "Sau.png", + }, +]; + +export const KannadaGunithas = [ + { + audio: "308e37dc-7a5b-4110-b3fd-9569bd82b588.wav", + image: "240cc0fc-84d7-426e-8e8e-a4167a4cf07c.png", + }, + { + audio: "9fc48b40-6ad8-4c29-9599-439fb7c62a47.wav", + image: "2eb6b9fd-d333-465a-a481-199ff2bfe27a.png", + }, + { + audio: "7383626d-6b2a-4010-98e1-cb1ab35b99be.wav", + image: "c856ee50-0643-45aa-bf51-5d4feb98f786.png", + }, + { + audio: "3512fafc-4479-4c7b-a622-09bc37e65377.wav", + image: "ddc67658-7b77-4cc4-a648-0eb43eb2bd1a.png", + }, + { + audio: "a3cfc183-00cc-4a6e-9c09-ea3c80c985de.wav", + image: "b72d9271-7e77-4bbe-ba30-432c512689e1.png", + }, + { + audio: "caef3f40-8425-4f76-8a85-f10810a21158.wav", + image: "ef3fbb39-7c73-4db8-a338-a528ac1060ea.png", + }, + { + audio: "e92eb339-f7a6-477a-b9ad-3328254f2581.wav", + image: "1f6f0c10-4eff-4393-9965-964566fe2025.png", + }, + { + audio: "2a4407d9-e4c9-4a25-9497-970272faedf9.wav", + image: "492f2b47-b5aa-4a54-b025-57f984211648.png", + }, + { + audio: "a9ac76c6-9baf-4462-8693-c8946d9fba74.wav", + image: "35c17022-d984-4c8d-b020-5f675db40676.png", + }, + { + audio: "88afc29e-4b5b-4101-b6bf-7bda2eb7a89d.wav", + image: "28e6bea9-de66-4020-a752-292830c7d4d9.png", + }, + { + audio: "19610e31-7d68-4576-9664-57115ffaa308.wav", + image: "7ecd088f-3a90-40a2-8c81-01ce34aeb93b.png", + }, + { + audio: "3c9ef757-37bf-4b5e-8881-9cc9ed1b687d.wav", + image: "2f0a6cd3-c186-45e3-bb69-5eeea8945e2d.png", + }, + { + audio: "c6528225-6aeb-454b-af78-ad4ce404ceff.wav", + image: "9b36cefc-f97b-4407-96db-2853016945e0.png", + }, + { + audio: "7d012300-9125-4a0b-8e39-5847efda0adf.wav", + image: "4685c085-539a-4783-8e53-1385bdba71e9.png", + }, + { + audio: "9aa231bf-47ea-43b7-87f8-8c768ff35281.wav", + image: "2122084b-6eb0-4518-85df-0d5dca3153fb.png", + }, + { + audio: "426cb98c-d48c-42b9-ac53-eed5eb3add3f.wav", + image: "a4db46bb-82a7-4f02-aeef-4f34d076e7b8.png", + }, + { + audio: "49f6314e-3c34-4cce-89dd-59f9bb957356.wav", + image: "b9317183-7bfc-4216-afa3-3c8b46d93452.png", + }, + { + audio: "c926b757-6438-4830-bc00-d6f0f2d08b9c.wav", + image: "f909d924-a6be-451a-a8db-75306465989d.png", + }, + { + audio: "2bb85642-0135-443c-a83a-4390acd3a26b.wav", + image: "73d404ed-1940-41bc-8a5d-d42d3fb40258.png", + }, + { + audio: "087e23f9-72a1-4889-8305-3c6c95f39485.wav", + image: "41d66b7b-d7bb-478f-8a60-63f63664aa86.png", + }, + { + audio: "36a5f61d-892d-4132-abd2-176cfec1279b.wav", + image: "eff49634-8bce-4f88-b46d-1bcfab2c5f71.png", + }, + { + audio: "16ce6a96-0c42-4598-b74d-e3fa6b29a3db.wav", + image: "371db4e5-a4a7-48b6-bd33-f7d490b358d9.png", + }, + { + audio: "05b3a5cb-47a5-4138-a40e-befdb4d6becc.wav", + image: "d313099d-c9c3-48d3-bb25-3b390c843ecc.png", + }, + { + audio: "7e07d85e-c34c-4517-9534-0c278396b0f5.wav", + image: "0221666b-8efc-4ae6-9fd9-521cd2dd5de4.png", + }, + { + audio: "99e9c9d2-c12e-4d6f-b66d-c7f4750fd4de.wav", + image: "d6a83829-aa7c-4b09-8ba8-b38d57b1ebd3.png", + }, + { + audio: "448d3297-6a30-401a-9b6d-a2beca4f9d23.wav", + image: "9741a6ba-4da7-4e91-bb09-3d178f64202d.png", + }, + { + audio: "45ebb5e2-efa4-4b68-a6b8-5117f0255e4d.wav", + image: "52bead5e-48dd-4d73-8cb1-e9868b8b0fc8.png", + }, + { + audio: "01b049ee-f204-4016-832a-a02f993974aa.wav", + image: "31d61c84-7207-479f-b82e-195b18104d2d.png", + }, + { + audio: "c0066b53-04b4-47de-927e-b32e853e2d2c.wav", + image: "ce502389-2462-4165-a02e-3af5ddb8d253.png", + }, + { + audio: "ce5767f5-6fbb-43f8-be3a-16ea8682a4e2.wav", + image: "f854e4a0-3740-4af8-a911-cd3b9b861bb7.png", + }, + { + audio: "6cb6c7dc-b3d8-4312-9e7e-d5146f6e295f.wav", + image: "40639681-dcb9-4f67-95c9-35fa61db1102.png", + }, + { + audio: "78ee1aec-9cd5-4681-b5c5-60d139a0d3c0.wav", + image: "bed78664-f7a3-4fe9-82ea-804242be602f.png", + }, +]; + function getScriptFromLang(lang) { const scriptMap = { hi: "devanagari", @@ -2364,6 +4121,7 @@ const Barakhadi = ({ currentImg, vocabCount, wordCount, + customWords, // Array of words to filter (e.g., ["నది", "చేప", "చీర"] for Telugu F2) }) => { steps = 1; @@ -2374,6 +4132,7 @@ const Barakhadi = ({ const [currentBarakhadi, setCurrentBarakhadi] = useState({}); const [currentWordIndex, setCurrentWordIndex] = useState(0); const [incorrectCell, setIncorrectCell] = useState(null); + const [isWordWrong, setIsWordWrong] = useState(false); const [voicesReady, setVoicesReady] = useState(false); const [voiceStatus, setVoiceStatus] = useState(""); const navigate = useNavigate(); @@ -2490,13 +4249,47 @@ const Barakhadi = ({ return wordData[lang] || wordData.hi; }; - const wordDataList = getWordData(); + // Get all words for the language + let wordDataList = getWordData(); + + // Filter wordDataList based on customWords if provided (similar to how LetterTrain filters with customLetters) + if (customWords && Array.isArray(customWords) && customWords.length > 0) { + // Normalize customWords for comparison (trim whitespace) + const normalizedCustomWords = customWords + .map((word) => (word && typeof word === "string" ? word.trim() : "")) + .filter(Boolean); + + console.log("Barakhadi - Filtering words based on customWords:", { + customWords, + normalizedCustomWords, + totalWordsBeforeFilter: wordDataList.length, + }); + + // Filter wordDataList to only include words that match customWords + wordDataList = wordDataList.filter((wordItem) => { + const wordText = wordItem?.text; + if (!wordText || typeof wordText !== "string") { + return false; + } + // Check if the word text matches any of the custom words + return normalizedCustomWords.includes(wordText.trim()); + }); + + console.log("Barakhadi - Filtered wordDataList:", { + filteredWordsCount: wordDataList.length, + filteredWords: wordDataList.map((item) => item.text), + }); + } else { + console.log("Barakhadi - No customWords provided, using all words:", { + totalWords: wordDataList.length, + }); + } const getTitle = () => { const titles = { hi: "हिंदी बारहखड़ी चार्ट", ta: "தமிழ் பாராகடி சார்ட்", - te: "తెలుగు బారాఖడీ చార్ట్", + te: "తెలుగు గుణింతాలు చార్ట్", kn: "ಕನ್ನಡ ಬಾರಾಖಡಿ ಚಾರ್ಟ್", }; return titles[lang] || titles.hi; @@ -2538,7 +4331,7 @@ const Barakhadi = ({ const titles = { hi: "हिंदी बारहखड़ी चार्ट", ta: "தமிழ் மெய்யெழுத்துக்கள்", - te: "తెలుగు బారాఖడీ చార్ట్", + te: "తెలుగు గుణింతాలు చార్ట్", kn: "ಕನ್ನಡ ಬಾರಾಖಡಿ ಚಾರ್ಟ್", }; return titles[lang] || titles.hi; @@ -2595,11 +4388,20 @@ const Barakhadi = ({ }; useEffect(() => { - const initialTargetWord = wordDataList[0].text; - setTargetWord(initialTargetWord); - const barakhadi = getBarakhadiForWord(initialTargetWord, lang); - setCurrentBarakhadi(barakhadi); - }, [lang]); + // Only set initial word if wordDataList has items + if (wordDataList && wordDataList.length > 0) { + const initialTargetWord = wordDataList[0].text; + setTargetWord(initialTargetWord); + const barakhadi = getBarakhadiForWord(initialTargetWord, lang); + setCurrentBarakhadi(barakhadi); + setCurrentWordIndex(0); // Reset word index when word list changes + } else { + console.warn( + "Barakhadi - wordDataList is empty, cannot set initial target word" + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [lang, customWords]); // wordDataList is computed from lang and customWords, so we don't need it in deps useEffect(() => { if (targetWord) { @@ -2609,31 +4411,10 @@ const Barakhadi = ({ }, [targetWord, lang]); useEffect(() => { - (async () => { - try { - const lang = getLocalData("lang"); - // Fetch assessment data - const resAssessment = await fetchAssessmentData(lang); - const sentences = resAssessment?.data?.find( - (elem) => elem.category === "Char" - ); - - if (!sentences?.collectionId) { - console.error("No collection ID found for sentences."); - return; - } - - const resPagination = await fetchPaginatedContent( - sentences.collectionId, - 10 - ); - - setTotalSyllableCount(resPagination?.totalSyllableCount); - setCurrentCollectionId(sentences?.collectionId); - } catch (error) { - console.error("Error fetching data:", error); - } - })(); + // Removed unnecessary getAssessment & Pagination API calls + console.log( + "Barakhadi component mounted - skipping assessment/pagination API calls" + ); }, []); const handleCompletion = async () => { @@ -2664,17 +4445,7 @@ const Barakhadi = ({ console.error("Error creating learner progress:", error); } - try { - const getSetResultRes = await fetchGetSetResult( - sub_session_id, - currentContentType, - currentCollectionId, - totalSyllableCount - ); - console.log("GetSet result:", getSetResultRes); - } catch (error) { - console.error("Error fetching set result:", error); - } + // Removed fetchGetSetResult call since currentCollectionId and totalSyllableCount if (!(localStorage.getItem("contentSessionId") !== null)) { let point = 1; @@ -2709,43 +4480,109 @@ const Barakhadi = ({ navigate("/discover-start"); }; - const handleNextWord = async () => { + const handleNextWord = () => { + // Safety check: ensure wordDataList has items + if (!wordDataList || wordDataList.length === 0) { + console.warn("Barakhadi - handleNextWord: wordDataList is empty"); + // If handleNext prop is provided (e.g., from F2 flow), use it instead of default navigation + if (handleNext && typeof handleNext === "function") { + handleNext(); + } else { + // Default behavior for non-flow usage + setLocalData("rFlow", false); + setLocalData("mFail", false); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + callTelemetryDiscovery("R1-Barakhadi"); + } + return; + } + + console.log("Barakhadi handleNextWord:", { + currentWordIndex, + wordDataListLength: wordDataList.length, + isLastItem: currentWordIndex >= wordDataList.length - 1, + customWords, + hasHandleNext: !!handleNext, + }); + const nextIndex = currentWordIndex + 1; - if (nextIndex >= wordDataList.length) { - setLocalData("rFlow", false); - setLocalData("mFail", false); - if (level === "B") { - await handleCompletion(); - navigate("/discover-end"); - return; + // Check if we've reached the end of the word list + if (nextIndex < wordDataList.length) { + // Safety check: ensure nextIndex is valid and has text property + if (wordDataList[nextIndex] && wordDataList[nextIndex].text) { + // Move to next word + setCurrentWordIndex(nextIndex); + setTargetWord(wordDataList[nextIndex].text); + setWord(""); + setShowConfetti(false); + setIncorrectCell(null); + setIsWordWrong(false); + } else { + console.warn( + "Barakhadi - handleNextWord: Invalid nextIndex or missing text", + { + nextIndex, + wordDataListLength: wordDataList.length, + wordDataListNextIndex: wordDataList[nextIndex], + } + ); + // If invalid, treat as completion + if (handleNext && typeof handleNext === "function") { + handleNext(); + } else { + setLocalData("rFlow", false); + setLocalData("mFail", false); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + callTelemetryDiscovery("R1-Barakhadi"); + } } - if (process.env.REACT_APP_IS_APP_IFRAME === "true") { - navigate("/"); + } else { + // Reached end of word list - complete the Barakhadi step + console.log( + "Barakhadi completed - all customWords done. Calling handleNext." + ); + // If handleNext prop is provided (e.g., from F2 flow), use it instead of default navigation + if (handleNext && typeof handleNext === "function") { + handleNext(); } else { - navigate("/discover-start"); + // Default behavior for non-flow usage (R0, R1, etc.) + setLocalData("rFlow", false); + setLocalData("mFail", false); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + callTelemetryDiscovery("R1-Barakhadi"); } - callTelemetryDiscovery("R1-Barakhadi"); - + // Don't continue - exit here return; } - - setCurrentWordIndex(nextIndex); - setTargetWord(wordDataList[nextIndex].text); - setWord(""); - setShowConfetti(false); - setIncorrectCell(null); }; const handleLetterClick = (letter, rowIndex, colIndex) => { - const remainingTarget = targetWord.slice(word.length); + // Use grapheme segmentation for Indic scripts to correctly compare + // multi-codepoint characters (e.g., "ది" should not match "ద") + const targetGraphemes = splitGraphemes(targetWord); + const wordGraphemes = splitGraphemes(word); + const nextExpectedGrapheme = targetGraphemes[wordGraphemes.length]; - const isCorrect = remainingTarget.startsWith(letter); + const isCorrect = letter === nextExpectedGrapheme; const newWord = word + letter; setWord(newWord); if (isCorrect) { + setIsWordWrong(false); if (newWord === targetWord) { correctAudio.play(); setShowConfetti(true); @@ -2753,24 +4590,53 @@ const Barakhadi = ({ setShowConfetti(false); }, 3000); } + } else { + // when user seclect wrong option then colour is red. + wrongAudio.play(); + setIsWordWrong(true); + setIncorrectCell({ rowIndex, colIndex }); + setTimeout(() => { + setIncorrectCell(null); + }, 500); } }; const handleErase = () => { setWord(""); setIncorrectCell(null); + setIsWordWrong(false); }; const handleDelete = () => { - setWord((prevWord) => prevWord.slice(0, -1)); + // Use grapheme segmentation to delete the last visual character, + // not just the last code point (important for Indic scripts) + const wordGraphemes = splitGraphemes(word); + wordGraphemes.pop(); + const newWord = wordGraphemes.join(""); + setWord(newWord); setIncorrectCell(null); + // Check if the remaining word is still correct so far using grapheme comparison + if (newWord.length > 0) { + const targetGraphemes = splitGraphemes(targetWord); + const newWordGraphemes = splitGraphemes(newWord); + const isCorrectSoFar = newWordGraphemes.every( + (g, i) => g === targetGraphemes[i] + ); + setIsWordWrong(!isCorrectSoFar); + } else { + setIsWordWrong(false); + } }; const handleListen = () => { if (word.length > 0) { console.log("Playing audio for user-typed word:", word); if (word === targetWord) { - playSegmentedAudio(currentWordData); + if (currentWordData) { + playSegmentedAudio(currentWordData); + } else { + playTTS(word); + } } else { playTTS(word); } @@ -2937,8 +4803,11 @@ const Barakhadi = ({ ); }; + // Safety check: ensure wordDataList has items before accessing const currentWordData = - wordDataList.find((item) => item.text === targetWord) || wordDataList[0]; + wordDataList && wordDataList.length > 0 + ? wordDataList.find((item) => item.text === targetWord) || wordDataList[0] + : null; const vyajan = Object.keys(currentBarakhadi); const generateFullBarakhadi = () => { @@ -3032,7 +4901,7 @@ const Barakhadi = ({ }; const tableStyle = { - marginTop: "10px", + marginTop: "50px", borderCollapse: "collapse", width: "100%", tableLayout: "fixed", @@ -3042,25 +4911,29 @@ const Barakhadi = ({ const tdStyle = { padding: "4px", border: "1px solid #ccc", - fontSize: "23px", + fontSize: lang === "te" ? "28px" : "23px", textAlign: "center", width: "58px", cursor: "pointer", - fontWeight: 800, + fontWeight: lang === "te" ? 400 : 800, + fontFamily: getFontFamily(lang), transition: "background-color 0.3s ease", }; const circleStyle = { - width: "22px", - height: "22px", + width: lang === "te" ? "28px" : "22px", + height: lang === "te" ? "28px" : "22px", borderRadius: "50%", background: "#2c3e50", color: "#fff", display: "flex", alignItems: "center", justifyContent: "center", - fontWeight: "bold", - fontSize: "12px", + fontWeight: lang === "te" ? "normal" : "bold", + fontSize: lang === "te" ? "16px" : "12px", + fontFamily: getFontFamily(lang), + lineHeight: "1", + textAlign: "center", boxShadow: "0px 4px 6px rgba(0,0,0,0.15)", margin: "0 auto", marginBottom: "4px", @@ -3068,16 +4941,19 @@ const Barakhadi = ({ const leftCircleStyle = { position: "absolute", - width: "28px", - height: "28px", + width: lang === "te" ? "34px" : "28px", + height: lang === "te" ? "34px" : "28px", borderRadius: "50%", background: "#ffeb3b", color: "#000", display: "flex", alignItems: "center", justifyContent: "center", - fontWeight: "bold", - fontSize: "12px", + fontWeight: lang === "te" ? "normal" : "bold", + fontSize: lang === "te" ? "18px" : "12px", + fontFamily: getFontFamily(lang), + lineHeight: "1", + textAlign: "center", boxShadow: "0px 4px 6px rgba(0,0,0,0.15)", border: "1px solid black", }; @@ -3300,19 +5176,22 @@ const Barakhadi = ({
{v} @@ -3340,18 +5219,21 @@ const Barakhadi = ({
{consonant} @@ -3379,9 +5261,11 @@ const Barakhadi = ({ width: 55, height: 55, border: "1px solid #ccc", - fontSize: "18px", - fontWeight: "bold", + fontSize: lang === "te" ? "28px" : "18px", + fontWeight: + lang === "te" ? "normal" : "bold", minWidth: "55px", + fontFamily: getFontFamily(lang), }} > {syllable} @@ -3403,10 +5287,10 @@ const Barakhadi = ({
{lang === "hi" @@ -3414,7 +5298,7 @@ const Barakhadi = ({ : lang === "ta" ? "சொல்லை உருவாக்கு" : lang === "te" - ? "పదం తయారు చేయండి" + ? "తెలుగు గుణింతాలు చార్ట్" : lang === "kn" ? "ಪದವನ್ನು ರಚಿಸಿ" : "Make a Word"} @@ -3452,14 +5336,19 @@ const Barakhadi = ({ style={{ background: "rgba(51, 63, 97, 1)", color: "#fff", - padding: "2px 24px", + padding: lang === "te" ? "0px 24px 8px 24px" : "2px 24px", borderRadius: "6px", minWidth: "80px", textAlign: "center", display: "inline-block", + fontFamily: getFontFamily(lang), + fontSize: lang === "te" ? "28px" : "20px", + fontWeight: "normal", + lineHeight: lang === "te" ? "1.1" : "normal", + verticalAlign: "middle", }} > - {currentWordData.text} + {currentWordData?.text || ""}
playWordAudio(currentWordData)} + onClick={() => + currentWordData && playWordAudio(currentWordData) + } />
{word ? word : } @@ -3626,6 +5528,7 @@ const Barakhadi = ({ style={{ position: "relative", paddingLeft: "60px", + marginTop: "30px", //height: "300px", }} > @@ -3655,17 +5558,28 @@ const Barakhadi = ({ {vyajan.map((consonant, rowIndex) => ( - {currentBarakhadi[consonant]?.map((cell, colIndex) => ( - - handleLetterClick(cell, rowIndex, colIndex) - } - > - {cell} - - ))} + {currentBarakhadi[consonant]?.map((cell, colIndex) => { + const isIncorrectCell = + incorrectCell?.rowIndex === rowIndex && + incorrectCell?.colIndex === colIndex; + return ( + + handleLetterClick(cell, rowIndex, colIndex) + } + > + {cell} + + ); + })} ))} diff --git a/src/RFlow/F1.jsx b/src/RFlow/F1.jsx new file mode 100644 index 00000000..c617b8ec --- /dev/null +++ b/src/RFlow/F1.jsx @@ -0,0 +1,189 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import R0 from "./R0"; +import { + getLocalData, + setLocalData, + levelGetContent, + practiceSteps, +} from "../utils/constants"; + +/** + * F1 Flow sequence: + * L=Learn (Letter Train), P=Practice (Letter Hunt), A=Apply (Letter Hunt with 3 levels) + * + * Structure: + * Learn 1 -> Practice 1 -> Learn 2 -> Practice 2 -> Learn 3 -> Practice 3 -> Apply 1 -> + * Learn 4 -> Practice 4 -> Learn 5 -> Practice 5 -> Learn 6 -> Practice 6 -> Apply 2 -> + * Learn 7 -> Practice 7 -> Learn 8 -> Practice 8 -> Learn 9 -> Practice 9 -> Apply 3 + * + * Apply steps have special handling: + * - Apply 1: 3 levels, if fail at any level → Learn 1, if pass all → Learn 4 + * - Apply 2: 3 levels, if fail at any level → Learn 4, if pass all → Learn 7 + * - Apply 3: 3 levels, if fail at any level → Learn 7, if pass all → F2 + */ +export const F1_FLOW = [ + { type: "L", step: 1 }, // Learn 1 - Letter Train + { type: "P", step: 1 }, // Practice 1 - Letter Hunt (1 level, 10 content) + { type: "L", step: 2 }, // Learn 2 - Letter Train + { type: "P", step: 2 }, // Practice 2 - Letter Hunt (1 level, 10 content) + { type: "L", step: 3 }, // Learn 3 - Letter Train + { type: "P", step: 3 }, // Practice 3 - Letter Hunt (1 level, 10 content) + { type: "A", step: 1, failRedirect: "L1", passRedirect: "L4" }, // Apply 1 - Letter Hunt (3 levels, 13 content per level) + { type: "L", step: 4 }, // Learn 4 - Letter Train + { type: "P", step: 4 }, // Practice 4 - Letter Hunt (1 level, 10 content) + { type: "L", step: 5 }, // Learn 5 - Letter Train + { type: "P", step: 5 }, // Practice 5 - Letter Hunt (1 level, 10 content) + { type: "L", step: 6 }, // Learn 6 - Letter Train + { type: "P", step: 6 }, // Practice 6 - Letter Hunt (1 level, 10 content) + { type: "A", step: 2, failRedirect: "L4", passRedirect: "L7" }, // Apply 2 - Letter Hunt (3 levels, 13 content per level) + { type: "L", step: 7 }, // Learn 7 - Letter Train + { type: "P", step: 7 }, // Practice 7 - Letter Hunt (1 level, 10 content) + { type: "L", step: 8 }, // Learn 8 - Letter Train + { type: "P", step: 8 }, // Practice 8 - Letter Hunt (1 level, 10 content) + { type: "L", step: 9 }, // Learn 9 - Letter Train + { type: "P", step: 9 }, // Practice 9 - Letter Hunt (1 level, 10 content) + { type: "A", step: 3, failRedirect: "L7", passRedirect: "F2" }, // Apply 3 - Letter Hunt (3 levels, 13 content per level) +]; + +/** + * Get current F1 flow step + */ +export const getF1FlowStep = () => { + const savedIndex = getLocalData("f1FlowIndex"); + const flowIndex = savedIndex ? Number(savedIndex) : 0; + return { + index: flowIndex, + step: F1_FLOW[flowIndex] || null, + isLast: flowIndex === F1_FLOW.length - 1, + }; +}; + +/** + * Advance to next F1 flow step + */ +export const advanceF1Flow = () => { + const current = getF1FlowStep(); + if (current.isLast) { + setLocalData("f1FlowIndex", null); + return null; + } + const nextIndex = current.index + 1; + setLocalData("f1FlowIndex", nextIndex); + return F1_FLOW[nextIndex]; +}; + +/** + * Reset F1 flow + */ +export const resetF1Flow = () => { + setLocalData("f1FlowIndex", null); +}; + +const F1 = ({ + setVoiceText, + setRecordedAudio, + setVoiceAnimate, + storyLine, + type, + handleNext, + background, + parentWords = "", + showTimer, + points, + steps, + currentStep, + contentId, + contentType, + level, + isDiscover, + progressData, + showProgress, + playTeacherAudio = () => {}, + callUpdateLearner, + disableScreen, + isShowCase, + handleBack, + loading, + setOpenMessageDialog, + audio, + currentImg, + vocabCount, + wordCount, + customLetters, +}) => { + const navigate = useNavigate(); + const lang = getLocalData("lang") || "en"; + + // Get current flow step from localStorage or default to 0 + const [currentFlowIndex, setCurrentFlowIndex] = useState(() => { + const savedIndex = getLocalData("f1FlowIndex"); + return savedIndex ? Number(savedIndex) : 0; + }); + + const currentFlowStep = F1_FLOW[currentFlowIndex]; + const isLastStep = currentFlowIndex === F1_FLOW.length - 1; + + // Save flow index to localStorage + useEffect(() => { + setLocalData("f1FlowIndex", currentFlowIndex); + }, [currentFlowIndex]); + + // Handle completion of current step + const handleStepComplete = () => { + if (isLastStep) { + // Flow complete - mark F1 as complete to trigger letter hunt + setLocalData("f1FlowIndex", null); // Reset flow + setLocalData("f1FlowComplete", "true"); // Mark F1 as complete + setLocalData("rStepZero", null); // Clear rStepZero to prevent showing R1 + // Don't navigate away - let Practice.jsx handle showing letter hunt + // Practice.jsx will detect f1FlowComplete and show letter hunt + } else { + // Move to next step + setCurrentFlowIndex((prev) => prev + 1); + } + }; + + // Handle Learn (L) step completion + const handleLearnComplete = () => { + // Clear rStepZero that R0 might have set + setLocalData("rStepZero", null); + handleStepComplete(); + }; + + // Render Learn step (Letter Train - uses LetterTrain component, not R0) + const renderLearnStep = () => { + const learnStepNumber = currentFlowStep.step; + + // Get custom letters from F1 config + // Learn steps map directly: L1->P1, L2->P4, L3->P7, L4->P10, L5->P13, L6->P16, L7->P19, L8->P22, L9->P25 + // But in practiceSteps, they're: L1->index 0, L2->index 3, L3->index 6, etc. + const f1Steps = levelGetContent[lang]?.F1 || []; + // Map Learn step number to practiceSteps index: L1->0, L2->3, L3->6, L4->9, L5->12, L6->15, L7->18, L8->21, L9->24 + const practiceStepIndex = (learnStepNumber - 1) * 3; // 0, 3, 6, 9, 12, 15, 18, 21, 24 + const stepConfig = f1Steps.find( + (elem) => elem.title === practiceSteps?.[practiceStepIndex]?.name + ); + const stepCustomLetters = stepConfig?.customLetters || customLetters; + + // Learn steps use LetterTrain component (not R0) + // Return null here - LetterTrain will be rendered by Practice.jsx based on mechanism + return null; + }; + + // Main render logic + if (!currentFlowStep) { + // F1 flow complete - return null so Practice.jsx can show letter hunt + if (getLocalData("f1FlowComplete") === "true") { + return null; + } + return
Flow step not found
; + } + + // For Learn, Practice, and Apply steps, return null + // These will be handled by the Practice component based on mechanism + // F1 component just tracks the flow index + return null; +}; + +export default F1; diff --git a/src/RFlow/F2.jsx b/src/RFlow/F2.jsx new file mode 100644 index 00000000..5761e44d --- /dev/null +++ b/src/RFlow/F2.jsx @@ -0,0 +1,172 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { + getLocalData, + setLocalData, + levelGetContent, + practiceSteps, +} from "../utils/constants"; + +/** + * F2 Flow sequence: + * L=Learn (Syllable Clap/Letter Train for English, Barakadi for Indic), P=Practice (Letter Hunt), A=Apply (Letter Hunt with 3 levels) + * + * Structure: + * Learn 1 -> Practice 1 -> Learn 2 -> Practice 2 -> Learn 3 -> Practice 3 -> Apply 1 -> + * Learn 4 -> Practice 4 -> Learn 5 -> Practice 5 -> Learn 6 -> Practice 6 -> Apply 2 -> + * Learn 7 -> Practice 7 -> Learn 8 -> Practice 8 -> Learn 9 -> Practice 9 -> Apply 3 + * + * Apply steps have special handling: + * - Apply 1: 3 levels, if fail at any level → Learn 1, if pass all → Learn 4 + * - Apply 2: 3 levels, if fail at any level → Learn 4, if pass all → Learn 7 + * - Apply 3: 3 levels, if fail at any level → Learn 7, if pass all → F3 (or complete) + */ +export const F2_FLOW = [ + { type: "L", step: 1 }, // Learn 1 - Syllable Clap/Letter Train (English) or Barakadi (Indic) + { type: "P", step: 1 }, // Practice 1 - Letter Hunt (1 level, 10 content) + { type: "L", step: 2 }, // Learn 2 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 2 }, // Practice 2 - Letter Hunt (1 level, 10 content) + { type: "L", step: 3 }, // Learn 3 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 3 }, // Practice 3 - Letter Hunt (1 level, 10 content) + { type: "A", step: 1, failRedirect: "L1", passRedirect: "L4" }, // Apply 1 - Letter Hunt (3 levels, 13 content per level) + { type: "L", step: 4 }, // Learn 4 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 4 }, // Practice 4 - Letter Hunt (1 level, 10 content) + { type: "L", step: 5 }, // Learn 5 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 5 }, // Practice 5 - Letter Hunt (1 level, 10 content) + { type: "L", step: 6 }, // Learn 6 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 6 }, // Practice 6 - Letter Hunt (1 level, 10 content) + { type: "A", step: 2, failRedirect: "L4", passRedirect: "L7" }, // Apply 2 - Letter Hunt (3 levels, 13 content per level) + { type: "L", step: 7 }, // Learn 7 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 7 }, // Practice 7 - Letter Hunt (1 level, 10 content) + { type: "L", step: 8 }, // Learn 8 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 8 }, // Practice 8 - Letter Hunt (1 level, 10 content) + { type: "L", step: 9 }, // Learn 9 - Syllable Clap/Letter Hunt (English) or Barakadi (Indic) + { type: "P", step: 9 }, // Practice 9 - Letter Hunt (1 level, 10 content) + { type: "A", step: 3, failRedirect: "L7", passRedirect: "F3" }, // Apply 3 - Letter Hunt (3 levels, 13 content per level) +]; + +/** + * Get current F2 flow step + */ +export const getF2FlowStep = () => { + const savedIndex = getLocalData("f2FlowIndex"); + const flowIndex = savedIndex ? Number(savedIndex) : 0; + return { + index: flowIndex, + step: F2_FLOW[flowIndex] || null, + isLast: flowIndex === F2_FLOW.length - 1, + }; +}; + +/** + * Advance to next F2 flow step + */ +export const advanceF2Flow = () => { + const current = getF2FlowStep(); + if (current.isLast) { + setLocalData("f2FlowIndex", null); + return null; + } + const nextIndex = current.index + 1; + setLocalData("f2FlowIndex", nextIndex); + return F2_FLOW[nextIndex]; +}; + +/** + * Reset F2 flow + */ +export const resetF2Flow = () => { + setLocalData("f2FlowIndex", null); +}; + +const F2 = ({ + setVoiceText, + setRecordedAudio, + setVoiceAnimate, + storyLine, + type, + handleNext, + background, + parentWords = "", + showTimer, + points, + steps, + currentStep, + contentId, + contentType, + level, + isDiscover, + progressData, + showProgress, + playTeacherAudio = () => {}, + callUpdateLearner, + disableScreen, + isShowCase, + handleBack, + loading, + setOpenMessageDialog, + audio, + currentImg, + vocabCount, + wordCount, + customLetters, +}) => { + const navigate = useNavigate(); + const lang = getLocalData("lang") || "en"; + + // Get current flow step from localStorage or default to 0 + const [currentFlowIndex, setCurrentFlowIndex] = useState(() => { + const savedIndex = getLocalData("f2FlowIndex"); + return savedIndex ? Number(savedIndex) : 0; + }); + + const currentFlowStep = F2_FLOW[currentFlowIndex]; + const isLastStep = currentFlowIndex === F2_FLOW.length - 1; + + // Save flow index to localStorage + useEffect(() => { + setLocalData("f2FlowIndex", currentFlowIndex); + }, [currentFlowIndex]); + + // Handle completion of current step + const handleStepComplete = () => { + if (isLastStep) { + // Flow complete - mark F2 as complete + setLocalData("f2FlowIndex", null); // Reset flow + setLocalData("f2FlowComplete", "true"); // Mark F2 as complete + } else { + // Move to next step + setCurrentFlowIndex((prev) => prev + 1); + } + }; + + // Handle Learn (L) step completion + const handleLearnComplete = () => { + handleStepComplete(); + }; + + // Render Learn step (Syllable Clap/Letter Train for English, Barakadi for Indic) + const renderLearnStep = () => { + // For F2, Learn steps use different components based on language + // English: Syllable Clap/Letter Train + // Indic: Barakadi + // Return null here - these will be rendered by Practice.jsx based on mechanism + return null; + }; + + // Main render logic + if (!currentFlowStep) { + // F2 flow complete - return null so Practice.jsx can handle next flow + if (getLocalData("f2FlowComplete") === "true") { + return null; + } + return
Flow step not found
; + } + + // For Learn, Practice, and Apply steps, return null + // These will be handled by the Practice component based on mechanism + // F2 component just tracks the flow index + return null; +}; + +export default F2; diff --git a/src/RFlow/F3.jsx b/src/RFlow/F3.jsx new file mode 100644 index 00000000..8f3ad496 --- /dev/null +++ b/src/RFlow/F3.jsx @@ -0,0 +1,82 @@ +import { getLocalData, setLocalData } from "../utils/constants"; + +/** + * F3 Flow sequence: + * P=Practice (Letter Launcher), A=Apply (Letter Launcher + Memory Challenge + Read Aloud) + * + * Structure: + * Practice 1 (Letter Speed) -> Practice 2 (Letter Speed) -> Practice 3 (Letter Speed) -> + * Practice 4 (Letter Speed) -> Practice 5 (Letter Speed) -> Apply 1 (Letter Speed 3 levels + Memory Challenge 3 levels) -> + * Practice 6 (Syllable Speed) -> Practice 7 (Syllable Speed) -> Practice 8 (Syllable Speed) -> + * Practice 9 (Syllable Speed) -> Practice 10 (Syllable Speed) -> Apply 2 (Syllable Speed 1 level + Memory Challenge 3 levels + Read Aloud) + * + * Apply steps have special handling: + * - Apply 1: Letter Speed (3 levels) -> Memory Challenge (3 levels) + * - If fail at any level → Practice 1 + * - If pass all → Practice 6 + * - Apply 2: Syllable Speed (1 level) -> Memory Challenge (3 levels) -> Read Aloud + * - If fail at any level → Practice 6 + * - If pass all → Complete F3 + */ +export const F3_FLOW = [ + { type: "P", step: 1, mechanism: "letterLauncher", contentType: "letter" }, // Practice 1 - Letter Speed (Letter Launcher) + { type: "P", step: 2, mechanism: "letterLauncher", contentType: "letter" }, // Practice 2 - Letter Speed (Letter Launcher) + { type: "P", step: 3, mechanism: "letterLauncher", contentType: "letter" }, // Practice 3 - Letter Speed (Letter Launcher) + { type: "P", step: 4, mechanism: "letterLauncher", contentType: "letter" }, // Practice 4 - Letter Speed (Letter Launcher) + { type: "P", step: 5, mechanism: "letterLauncher", contentType: "letter" }, // Practice 5 - Letter Speed (Letter Launcher) + { + type: "A", + step: 1, + mechanism: "letterLauncher", + contentType: "letter", + failRedirect: "P1", + passRedirect: "P6", + }, // Apply 1 - Letter Speed (3 levels) + Memory Challenge (3 levels) + { type: "P", step: 6, mechanism: "letterLauncher", contentType: "syllable" }, // Practice 6 - Syllable Speed (Letter Launcher) + { type: "P", step: 7, mechanism: "letterLauncher", contentType: "syllable" }, // Practice 7 - Syllable Speed (Letter Launcher) + { type: "P", step: 8, mechanism: "letterLauncher", contentType: "syllable" }, // Practice 8 - Syllable Speed (Letter Launcher) + { type: "P", step: 9, mechanism: "letterLauncher", contentType: "syllable" }, // Practice 9 - Syllable Speed (Letter Launcher) + { type: "P", step: 10, mechanism: "letterLauncher", contentType: "syllable" }, // Practice 10 - Syllable Speed (Letter Launcher) + { + type: "A", + step: 2, + mechanism: "letterLauncher", + contentType: "syllable", + failRedirect: "P6", + passRedirect: "complete", + }, // Apply 2 - Syllable Speed (1 level) + Memory Challenge (3 levels) + Read Aloud +]; + +/** + * Get current F3 flow step + */ +export const getF3FlowStep = () => { + const savedIndex = getLocalData("f3FlowIndex"); + const flowIndex = savedIndex ? Number(savedIndex) : 0; + return { + index: flowIndex, + step: F3_FLOW[flowIndex] || null, + isLast: flowIndex === F3_FLOW.length - 1, + }; +}; + +/** + * Advance to next F3 flow step + */ +export const advanceF3Flow = () => { + const current = getF3FlowStep(); + if (current.isLast) { + setLocalData("f3FlowIndex", null); + return null; + } + const nextIndex = current.index + 1; + setLocalData("f3FlowIndex", nextIndex); + return F3_FLOW[nextIndex]; +}; + +/** + * Reset F3 flow + */ +export const resetF3Flow = () => { + setLocalData("f3FlowIndex", null); +}; diff --git a/src/RFlow/LetterTrain.jsx b/src/RFlow/LetterTrain.jsx new file mode 100644 index 00000000..9c59957a --- /dev/null +++ b/src/RFlow/LetterTrain.jsx @@ -0,0 +1,7309 @@ +import React, { useState, useEffect, useRef, useMemo } from "react"; +import Confetti from "react-confetti"; +import * as Assets from "../utils/imageAudioLinks"; +import { + ThemeProvider, + createTheme, + useMediaQuery, + Grid, + Box, + CircularProgress, +} from "@mui/material"; +import MainLayout from "../components/Layouts.jsx/MainLayout"; +import listenImg from "../assets/listen.svg"; +// import Mic from "../assets/mikee.svg"; +// import Stop from "../assets/pausse.svg"; +import correctSound from "../assets/correct.wav"; +import wrongSound from "../assets/audio/wrong.wav"; +import RecordVoiceVisualizer from "../utils/RecordVoiceVisualizer"; +import { + practiceSteps, + getLocalData, + NextButtonRound, + RetryIcon, + setLocalData, +} from "../utils/constants"; +import { getFontFamily } from "../utils/fontUtils"; +import { useNavigate } from "react-router-dom"; +import { response } from "../services/telementryService"; +import { Typography, Stack, IconButton } from "@mui/material"; +import { ArrowRight, RotateCcw } from "lucide-react"; +import trainImg from "../assets/trainImg.svg"; +import { motion, AnimatePresence } from "framer-motion"; +import VoiceAnalyser from "../utils/VoiceAnalyser"; +import * as s3Assets from "../utils/rFlowS3Links"; +import { getAssetUrl } from "../utils/rFlowS3Links"; +import { getAssetAudioUrl } from "../utils/rFlowS3Links"; +import { ArrowLeft } from "lucide-react"; // or your icon library +import hintimg from "../assets/hintsicon.svg"; +import ZoomableImage from "../components/Practice/ZoomableImage"; +import { splitGraphemes } from "split-graphemes"; + +const theme = createTheme(); + +export const dataEn = [ + { + syllable: "he", + items: [ + { + id: 1, + title: "Syllable", + syllable: "he", + word: "Hear", + image: getAssetUrl(s3Assets.HearImg), + audio: getAssetAudioUrl(s3Assets.HearSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.HearSingleAudio), + }, + { + id: 2, + title: "Syllable", + syllable: "he", + word: "teacher", + image: getAssetUrl(s3Assets.teacherImg), + audio: getAssetAudioUrl(s3Assets.teacherAudio), + singleAudio: getAssetAudioUrl(s3Assets.teacherAudio), + }, + ], + }, + { + syllable: "in", + items: [ + { + id: 3, + title: "Syllable", + syllable: "in", + word: "Pin", + image: getAssetUrl(s3Assets.PinImg), + audio: getAssetAudioUrl(s3Assets.PinAudio), + singleAudio: getAssetAudioUrl(s3Assets.PinAudio), + }, + { + id: 4, + title: "Syllable", + syllable: "in", + word: "Winter", + image: getAssetUrl(s3Assets.WinterImg), + audio: getAssetAudioUrl(s3Assets.WinterAudio), + singleAudio: getAssetAudioUrl(s3Assets.WinterAudio), + }, + ], + }, + { + syllable: "an", + items: [ + { + id: 5, + title: "Syllable", + syllable: "an", + word: "Ant", + image: getAssetUrl(s3Assets.AntImg), + audio: getAssetAudioUrl(s3Assets.AntAudio), + singleAudio: getAssetAudioUrl(s3Assets.AntAudio), + }, + { + id: 6, + title: "Syllable", + syllable: "an", + word: "plant", + image: getAssetUrl(s3Assets.plantImg), + audio: getAssetAudioUrl(s3Assets.plantAudio), + singleAudio: getAssetAudioUrl(s3Assets.plantAudio), + }, + ], + }, + { + syllable: "at", + items: [ + { + id: 7, + title: "Syllable", + syllable: "at", + word: "atlas", + image: getAssetUrl(s3Assets.atlasImg), + audio: getAssetAudioUrl(s3Assets.atlasSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.atlasSingleAudio), + }, + { + id: 8, + title: "Syllable", + syllable: "at", + word: "Cattle", + image: getAssetUrl(s3Assets.CattleImg), + audio: getAssetAudioUrl(s3Assets.CattleAudio), + singleAudio: getAssetAudioUrl(s3Assets.CattleAudio), + }, + ], + }, + { + syllable: "or", + items: [ + { + id: 9, + title: "Syllable", + syllable: "or", + word: "orange", + image: getAssetUrl(s3Assets.orangeImg), + audio: getAssetAudioUrl(s3Assets.orangeAudio), + singleAudio: getAssetAudioUrl(s3Assets.orangeAudio), + }, + { + id: 10, + title: "Syllable", + syllable: "or", + word: "store", + image: getAssetUrl(s3Assets.storeImg), + audio: getAssetAudioUrl(s3Assets.storeAudio), + singleAudio: getAssetAudioUrl(s3Assets.storeAudio), + }, + ], + }, + { + syllable: "of", + items: [ + { + id: 11, + title: "Syllable", + syllable: "of", + word: "Office", + image: getAssetUrl(s3Assets.OfficeImg), + audio: getAssetAudioUrl(s3Assets.OfficeAudio), + singleAudio: getAssetAudioUrl(s3Assets.OfficeAudio), + }, + { + id: 12, + title: "Syllable", + syllable: "of", + word: "Soft", + image: getAssetUrl(s3Assets.SoftImg), + audio: getAssetAudioUrl(s3Assets.SoftAudio), + singleAudio: getAssetAudioUrl(s3Assets.SoftAudio), + }, + ], + }, + { + syllable: "it", + items: [ + { + id: 13, + title: "Syllable", + syllable: "it", + word: "sit", + image: getAssetUrl(s3Assets.sitImg), + audio: getAssetAudioUrl(s3Assets.sitSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.sitSingleAudio), + }, + { + id: 14, + title: "Syllable", + syllable: "it", + word: "fruits", + image: getAssetUrl(s3Assets.fruitsImg), + audio: getAssetAudioUrl(s3Assets.fruitsAudio), + singleAudio: getAssetAudioUrl(s3Assets.fruitsAudio), + }, + ], + }, + { + syllable: "as", + items: [ + { + id: 15, + title: "Syllable", + syllable: "as", + word: "Gas", + image: getAssetUrl(s3Assets.GasImg), + audio: getAssetAudioUrl(s3Assets.GasAudio), + singleAudio: getAssetAudioUrl(s3Assets.GasAudio), + }, + { + id: 16, + title: "Syllable", + syllable: "as", + word: "basket", + image: getAssetUrl(s3Assets.basketImg), + audio: getAssetAudioUrl(s3Assets.basketAudio), + singleAudio: getAssetAudioUrl(s3Assets.basketAudio), + }, + ], + }, + { + syllable: "me", + items: [ + { + id: 17, + title: "Syllable", + syllable: "me", + word: "meat", + image: getAssetUrl(s3Assets.meatImg), + audio: getAssetAudioUrl(s3Assets.meatAudio), + singleAudio: getAssetAudioUrl(s3Assets.meatAudio), + }, + { + id: 18, + title: "Syllable", + syllable: "me", + word: "Camel", + image: getAssetUrl(s3Assets.CamelImg), + audio: getAssetAudioUrl(s3Assets.CamelAudio), + singleAudio: getAssetAudioUrl(s3Assets.CamelAudio), + }, + ], + }, + { + syllable: "be", + items: [ + { + id: 19, + title: "Syllable", + syllable: "be", + word: "bear", + image: getAssetUrl(s3Assets.bearImg), + audio: getAssetAudioUrl(s3Assets.bearAudio), + singleAudio: getAssetAudioUrl(s3Assets.bearAudio), + }, + { + id: 20, + title: "Syllable", + syllable: "be", + word: "beautiful", + image: getAssetUrl(s3Assets.beautifulImg), + audio: getAssetAudioUrl(s3Assets.beautifulAudio), + singleAudio: getAssetAudioUrl(s3Assets.beautifulAudio), + }, + ], + }, + { + syllable: "her", + items: [ + { + id: 21, + title: "Syllable", + syllable: "her", + word: "feather", + image: getAssetUrl(s3Assets.featherImg), + audio: getAssetAudioUrl(s3Assets.featherAudio), + singleAudio: getAssetAudioUrl(s3Assets.featherAudio), + }, + { + id: 22, + title: "Syllable", + syllable: "her", + word: "Mother", + image: getAssetUrl(s3Assets.MotherImg), + audio: getAssetAudioUrl(s3Assets.MotherSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.MotherSingleAudio), + }, + ], + }, + { + syllable: "ate", + items: [ + { + id: 23, + title: "Syllable", + syllable: "ate", + word: "Date", + image: getAssetUrl(s3Assets.DateImg), + audio: getAssetAudioUrl(s3Assets.DateAudio), + singleAudio: getAssetAudioUrl(s3Assets.DateAudio), + }, + { + id: 24, + title: "Syllable", + syllable: "ate", + word: "Gate", + image: getAssetUrl(s3Assets.GateImg), + audio: getAssetAudioUrl(s3Assets.GateAudio), + singleAudio: getAssetAudioUrl(s3Assets.GateAudio), + }, + ], + }, + { + syllable: "all", + items: [ + { + id: 25, + title: "Syllable", + syllable: "all", + word: "Tall", + image: getAssetUrl(s3Assets.TallImg), + audio: getAssetAudioUrl(s3Assets.TallAudio), + singleAudio: getAssetAudioUrl(s3Assets.TallAudio), + }, + { + id: 26, + title: "Syllable", + syllable: "all", + word: "Balloon", + image: getAssetUrl(s3Assets.BalloonImg), + audio: getAssetAudioUrl(s3Assets.BalloonAudio), + singleAudio: getAssetAudioUrl(s3Assets.BalloonAudio), + }, + ], + }, + { + syllable: "men", + items: [ + { + id: 27, + title: "Syllable", + syllable: "men", + word: "Menu", + image: getAssetUrl(s3Assets.MenuImg), + audio: getAssetAudioUrl(s3Assets.MenuAudio), + singleAudio: getAssetAudioUrl(s3Assets.MenuAudio), + }, + { + id: 28, + title: "Syllable", + syllable: "men", + word: "Women", + image: getAssetUrl(s3Assets.WomenImg), + audio: getAssetAudioUrl(s3Assets.WomenAudio), + singleAudio: getAssetAudioUrl(s3Assets.WomenAudio), + }, + ], + }, + { + syllable: "ear", + items: [ + { + id: 29, + title: "Syllable", + syllable: "ear", + word: "hear", + image: getAssetUrl(s3Assets.hearImg), + audio: getAssetAudioUrl(s3Assets.hearAudio), + singleAudio: getAssetAudioUrl(s3Assets.hearAudio), + }, + { + id: 30, + title: "Syllable", + syllable: "ear", + word: "Near", + image: getAssetUrl(s3Assets.NearImg), + audio: getAssetAudioUrl(s3Assets.NearAudio), + singleAudio: getAssetAudioUrl(s3Assets.NearAudio), + }, + ], + }, + { + syllable: "rat", + items: [ + { + id: 31, + title: "Syllable", + syllable: "rat", + word: "Rattle", + image: getAssetUrl(s3Assets.RattleImg), + audio: getAssetAudioUrl(s3Assets.RattleAudio), + singleAudio: getAssetAudioUrl(s3Assets.RattleAudio), + }, + { + id: 32, + title: "Syllable", + syllable: "rat", + word: "decorate", + image: getAssetUrl(s3Assets.decorateImg), + audio: getAssetAudioUrl(s3Assets.decorateAudio), + singleAudio: getAssetAudioUrl(s3Assets.decorateAudio), + }, + ], + }, + { + syllable: "Up", + items: [ + { + id: 33, + title: "Syllable", + syllable: "Up", + word: "Upset", + image: getAssetUrl(s3Assets.UpsetImg), + audio: getAssetAudioUrl(s3Assets.UpsetAudio), + singleAudio: getAssetAudioUrl(s3Assets.UpsetAudio), + }, + { + id: 34, + title: "Syllable", + syllable: "Up", + word: "Puppy", + image: getAssetUrl(s3Assets.PuppyImg), + audio: getAssetAudioUrl(s3Assets.PuppyAudio), + singleAudio: getAssetAudioUrl(s3Assets.PuppyAudio), + }, + ], + }, + { + syllable: "Us", + items: [ + { + id: 35, + title: "Syllable", + syllable: "Us", + word: "Bus", + image: getAssetUrl(s3Assets.BusImg), + audio: getAssetAudioUrl(s3Assets.BusAudio), + singleAudio: getAssetAudioUrl(s3Assets.BusAudio), + }, + { + id: 36, + title: "Syllable", + syllable: "Us", + word: "Dust", + image: getAssetUrl(s3Assets.DustImg), + audio: getAssetAudioUrl(s3Assets.DustAudio), + singleAudio: getAssetAudioUrl(s3Assets.DustAudio), + }, + ], + }, + { + syllable: "Am", + items: [ + { + id: 37, + title: "Syllable", + syllable: "Am", + word: "Jam", + image: getAssetUrl(s3Assets.JamImg), + audio: getAssetAudioUrl(s3Assets.JamAudio), + singleAudio: getAssetAudioUrl(s3Assets.JamAudio), + }, + { + id: 38, + title: "Syllable", + syllable: "Am", + word: "Camel", + image: getAssetUrl(s3Assets.Camel2Img), + audio: getAssetAudioUrl(s3Assets.Camel2Audio), + singleAudio: getAssetAudioUrl(s3Assets.Camel2Audio), + }, + ], + }, + { + syllable: "My", + items: [ + { + id: 39, + title: "Syllable", + syllable: "My", + word: "Mynah", + image: getAssetUrl(s3Assets.MynahImg), + audio: getAssetAudioUrl(s3Assets.MynahAudio), + singleAudio: getAssetAudioUrl(s3Assets.MynahAudio), + }, + { + id: 40, + title: "Syllable", + syllable: "My", + word: "Tummy", + image: getAssetUrl(s3Assets.TummyImg), + audio: getAssetAudioUrl(s3Assets.TummyAudio), + singleAudio: getAssetAudioUrl(s3Assets.TummyAudio), + }, + ], + }, + { + syllable: "So", + items: [ + { + id: 41, + title: "Syllable", + syllable: "So", + word: "Sofa", + image: getAssetUrl(s3Assets.SofaImg), + audio: getAssetAudioUrl(s3Assets.SofaAudio), + singleAudio: getAssetAudioUrl(s3Assets.SofaAudio), + }, + { + id: 42, + title: "Syllable", + syllable: "So", + word: "Person", + image: getAssetUrl(s3Assets.PersonImg), + audio: getAssetAudioUrl(s3Assets.PersonAudio), + singleAudio: getAssetAudioUrl(s3Assets.PersonAudio), + }, + ], + }, + { + syllable: "No", + items: [ + { + id: 43, + title: "Syllable", + syllable: "No", + word: "Nose", + image: getAssetUrl(s3Assets.NoseImg), + audio: getAssetAudioUrl(s3Assets.NoseAudio), + singleAudio: getAssetAudioUrl(s3Assets.NoseAudio), + }, + { + id: 44, + title: "Syllable", + syllable: "No", + word: "Piano", + image: getAssetUrl(s3Assets.PianoImg), + audio: getAssetAudioUrl(s3Assets.PianoAudio), + singleAudio: getAssetAudioUrl(s3Assets.PianoAudio), + }, + ], + }, + { + syllable: "is", + items: [ + { + id: 45, + title: "Syllable", + syllable: "is", + word: "list", + image: getAssetUrl(s3Assets.listImg), + audio: getAssetAudioUrl(s3Assets.listAudio), + singleAudio: getAssetAudioUrl(s3Assets.listAudio), + }, + { + id: 46, + title: "Syllable", + syllable: "is", + word: "fish", + image: getAssetUrl(s3Assets.fishImg), + audio: getAssetAudioUrl(s3Assets.fishAudio), + singleAudio: getAssetAudioUrl(s3Assets.fishAudio), + }, + ], + }, + { + syllable: "Ox", + items: [ + { + id: 47, + title: "Syllable", + syllable: "Ox", + word: "Oxen", + image: getAssetUrl(s3Assets.OxenImg), + audio: getAssetAudioUrl(s3Assets.OxenAudio), + singleAudio: getAssetAudioUrl(s3Assets.OxenAudio), + }, + { + id: 48, + title: "Syllable", + syllable: "Ox", + word: "Boxer", + image: getAssetUrl(s3Assets.BoxerImg), + audio: getAssetAudioUrl(s3Assets.BoxerAudio), + singleAudio: getAssetAudioUrl(s3Assets.BoxerAudio), + }, + ], + }, + { + syllable: "Do", + items: [ + { + id: 49, + title: "Syllable", + syllable: "Do", + word: "Doll", + image: getAssetUrl(s3Assets.DollImg), + audio: getAssetAudioUrl(s3Assets.DollAudio), + singleAudio: getAssetAudioUrl(s3Assets.DollAudio), + }, + { + id: 50, + title: "Syllable", + syllable: "Do", + word: "Indoor", + image: getAssetUrl(s3Assets.IndoorImg), + audio: getAssetAudioUrl(s3Assets.IndoorAudio), + singleAudio: getAssetAudioUrl(s3Assets.IndoorAudio), + }, + ], + }, + { + syllable: "Go", + items: [ + { + id: 51, + title: "Syllable", + syllable: "Go", + word: "Gold", + image: getAssetUrl(s3Assets.GoldImg), + audio: getAssetAudioUrl(s3Assets.GoldAudio), + singleAudio: getAssetAudioUrl(s3Assets.GoldAudio), + }, + { + id: 52, + title: "Syllable", + syllable: "Go", + word: "Wagon", + image: getAssetUrl(s3Assets.WagonImg), + audio: getAssetAudioUrl(s3Assets.WagonAudio), + singleAudio: getAssetAudioUrl(s3Assets.WagonAudio), + }, + ], + }, + { + syllable: "on", + items: [ + { + id: 53, + title: "Syllable", + syllable: "on", + word: "Online", + image: getAssetUrl(s3Assets.OnlineImg), + audio: getAssetAudioUrl(s3Assets.OnlineAudio), + singleAudio: getAssetAudioUrl(s3Assets.OnlineAudio), + }, + { + id: 54, + title: "Syllable", + syllable: "on", + word: "tongue", + image: getAssetUrl(s3Assets.tongueImg), + audio: getAssetAudioUrl(s3Assets.tongueAudio), + singleAudio: getAssetAudioUrl(s3Assets.tongueAudio), + }, + ], + }, + { + syllable: "to", + items: [ + { + id: 55, + title: "Syllable", + syllable: "to", + word: "tomorrow", + image: getAssetUrl(s3Assets.tomorrowImg), + audio: getAssetAudioUrl(s3Assets.tomorrowAudio), + singleAudio: getAssetAudioUrl(s3Assets.tomorrowAudio), + }, + { + id: 56, + title: "Syllable", + syllable: "to", + word: "store", + image: getAssetUrl(s3Assets.store2Img), + audio: getAssetAudioUrl(s3Assets.store2Audio), + singleAudio: getAssetAudioUrl(s3Assets.store2Audio), + }, + ], + }, + { + syllable: "Act", + items: [ + { + id: 57, + title: "Syllable", + syllable: "Act", + word: "Actor", + image: getAssetUrl(s3Assets.ActorImg), + audio: getAssetAudioUrl(s3Assets.ActorAudio), + singleAudio: getAssetAudioUrl(s3Assets.ActorAudio), + }, + { + id: 58, + title: "Syllable", + syllable: "Act", + word: "Tractor", + image: getAssetUrl(s3Assets.TractorImg), + audio: getAssetAudioUrl(s3Assets.TractorAudio), + singleAudio: getAssetAudioUrl(s3Assets.TractorAudio), + }, + ], + }, + { + syllable: "Age", + items: [ + { + id: 59, + title: "Syllable", + syllable: "Age", + word: "Agent", + image: getAssetUrl(s3Assets.AgentImg), + audio: getAssetAudioUrl(s3Assets.AgentAudio), + singleAudio: getAssetAudioUrl(s3Assets.AgentAudio), + }, + { + id: 60, + title: "Syllable", + syllable: "Age", + word: "Cage", + image: getAssetUrl(s3Assets.CageImg), + audio: getAssetAudioUrl(s3Assets.CageAudio), + singleAudio: getAssetAudioUrl(s3Assets.CageAudio), + }, + ], + }, + { + syllable: "Air", + items: [ + { + id: 61, + title: "Syllable", + syllable: "Air", + word: "Airport", + image: getAssetUrl(s3Assets.AirportImg), + audio: getAssetAudioUrl(s3Assets.AirportAudio), + singleAudio: getAssetAudioUrl(s3Assets.AirportAudio), + }, + { + id: 62, + title: "Syllable", + syllable: "Air", + word: "Chair", + image: getAssetUrl(s3Assets.ChairImg), + audio: getAssetAudioUrl(s3Assets.ChairAudio), + singleAudio: getAssetAudioUrl(s3Assets.ChairAudio), + }, + ], + }, + { + syllable: "Arm", + items: [ + { + id: 63, + title: "Syllable", + syllable: "Arm", + word: "Army", + image: getAssetUrl(s3Assets.Army2Img), + audio: getAssetAudioUrl(s3Assets.Army2Audio), + singleAudio: getAssetAudioUrl(s3Assets.Army2Audio), + }, + { + id: 64, + title: "Syllable", + syllable: "Arm", + word: "Farmer", + image: getAssetUrl(s3Assets.FarmerImg), + audio: getAssetAudioUrl(s3Assets.FarmerAudio), + singleAudio: getAssetAudioUrl(s3Assets.FarmerAudio), + }, + ], + }, + { + syllable: "Art", + items: [ + { + id: 65, + title: "Syllable", + syllable: "Art", + word: "Artist", + image: getAssetUrl(s3Assets.ArtistImg), + audio: getAssetAudioUrl(s3Assets.ArtistAudio), + singleAudio: getAssetAudioUrl(s3Assets.ArtistAudio), + }, + { + id: 66, + title: "Syllable", + syllable: "Art", + word: "Party", + image: getAssetUrl(s3Assets.PartyImg), + audio: getAssetAudioUrl(s3Assets.PartyAudio), + singleAudio: getAssetAudioUrl(s3Assets.PartyAudio), + }, + ], + }, + { + syllable: "Ask", + items: [ + { + id: 67, + title: "Syllable", + syllable: "Ask", + word: "Asking", + image: getAssetUrl(s3Assets.AskingImg), + audio: getAssetAudioUrl(s3Assets.AskingAudio), + singleAudio: getAssetAudioUrl(s3Assets.AskingAudio), + }, + { + id: 68, + title: "Syllable", + syllable: "Ask", + word: "Mask", + image: getAssetUrl(s3Assets.MaskImage), + audio: getAssetAudioUrl(s3Assets.MaskAudio), + singleAudio: getAssetAudioUrl(s3Assets.MaskAudio), + }, + ], + }, + { + syllable: "Bit", + items: [ + { + id: 69, + title: "Syllable", + syllable: "Bit", + word: "Bitter", + image: getAssetUrl(s3Assets.BitterImg), + audio: getAssetAudioUrl(s3Assets.BitterAudio), + singleAudio: getAssetAudioUrl(s3Assets.BitterAudio), + }, + { + id: 70, + title: "Syllable", + syllable: "Bit", + word: "Habit", + image: getAssetUrl(s3Assets.HabitImg), + audio: getAssetAudioUrl(s3Assets.HabitAudio), + singleAudio: getAssetAudioUrl(s3Assets.HabitAudio), + }, + ], + }, + { + syllable: "Car", + items: [ + { + id: 71, + title: "Syllable", + syllable: "Car", + word: "Carrot", + image: getAssetUrl(s3Assets.CarrotImg), + audio: getAssetAudioUrl(s3Assets.CarrotAudio), + singleAudio: getAssetAudioUrl(s3Assets.CarrotAudio), + }, + { + id: 72, + title: "Syllable", + syllable: "Car", + word: "Scarf", + image: getAssetUrl(s3Assets.ScarfImg), + audio: getAssetAudioUrl(s3Assets.ScarfAudio), + singleAudio: getAssetAudioUrl(s3Assets.ScarfAudio), + }, + ], + }, + { + syllable: "Fit", + items: [ + { + id: 73, + title: "Syllable", + syllable: "Fit", + word: "Fitness", + image: getAssetUrl(s3Assets.FitnessImg), + audio: getAssetAudioUrl(s3Assets.FitnessAudio), + singleAudio: getAssetAudioUrl(s3Assets.FitnessAudio), + }, + { + id: 74, + title: "Syllable", + syllable: "Fit", + word: "Profit", + image: getAssetUrl(s3Assets.ProfitImg), + audio: getAssetAudioUrl(s3Assets.ProfitAudio), + singleAudio: getAssetAudioUrl(s3Assets.ProfitAudio), + }, + ], + }, + { + syllable: "Fly", + items: [ + { + id: 75, + title: "Syllable", + syllable: "Fly", + word: "Flying", + image: getAssetUrl(s3Assets.FlyingImg), + audio: getAssetAudioUrl(s3Assets.FlyingAudio), + singleAudio: getAssetAudioUrl(s3Assets.FlyingAudio), + }, + { + id: 76, + title: "Syllable", + syllable: "Fly", + word: "Butterfly", + image: getAssetUrl(s3Assets.ButterflyImg), + audio: getAssetAudioUrl(s3Assets.ButterflyAudio), + singleAudio: getAssetAudioUrl(s3Assets.ButterflyAudio), + }, + ], + }, + { + syllable: "Fun", + items: [ + { + id: 77, + title: "Syllable", + syllable: "Fun", + word: "Funny", + image: getAssetUrl(s3Assets.FunnyImg), + audio: getAssetAudioUrl(s3Assets.FunnyAudio), + singleAudio: getAssetAudioUrl(s3Assets.FunnyAudio), + }, + { + id: 78, + title: "Syllable", + syllable: "Fun", + word: "Funnel", + image: getAssetUrl(s3Assets.FunnelImg), + audio: getAssetAudioUrl(s3Assets.FunnelAudio), + singleAudio: getAssetAudioUrl(s3Assets.FunnelAudio), + }, + ], + }, + { + syllable: "Ice", + items: [ + { + id: 79, + title: "Syllable", + syllable: "Ice", + word: "Mice", + image: getAssetUrl(s3Assets.MiceImg), + audio: getAssetAudioUrl(s3Assets.MiceAudio), + singleAudio: getAssetAudioUrl(s3Assets.MiceAudio), + }, + { + id: 80, + title: "Syllable", + syllable: "Ice", + word: "Juice", + image: getAssetUrl(s3Assets.JuiceImg), + audio: getAssetAudioUrl(s3Assets.JuiceAudio), + singleAudio: getAssetAudioUrl(s3Assets.JuiceAudio), + }, + ], + }, + { + syllable: "Ink", + items: [ + { + id: 81, + title: "Syllable", + syllable: "Ink", + word: "Pink", + image: getAssetUrl(s3Assets.PinkImg), + audio: getAssetAudioUrl(s3Assets.PinkAudio), + singleAudio: getAssetAudioUrl(s3Assets.PinkAudio), + }, + { + id: 82, + title: "Syllable", + syllable: "Ink", + word: "Twinkle", + image: getAssetUrl(s3Assets.TwinkleImg), + audio: getAssetAudioUrl(s3Assets.TwinkleAudio), + singleAudio: getAssetAudioUrl(s3Assets.TwinkleAudio), + }, + ], + }, + { + syllable: "Key", + items: [ + { + id: 83, + title: "Syllable", + syllable: "Key", + word: "Keyboard", + image: getAssetUrl(s3Assets.KeyboardImg), + audio: getAssetAudioUrl(s3Assets.KeyboardAudio), + singleAudio: getAssetAudioUrl(s3Assets.KeyboardAudio), + }, + { + id: 84, + title: "Syllable", + syllable: "Key", + word: "Donkey", + image: getAssetUrl(s3Assets.DonkeyImg), + audio: getAssetAudioUrl(s3Assets.DonkeyAudio), + singleAudio: getAssetAudioUrl(s3Assets.DonkeyAudio), + }, + ], + }, + { + syllable: "the", + items: [ + { + id: 85, + title: "Syllable", + syllable: "the", + word: "thief", + image: getAssetUrl(s3Assets.theifImg), + audio: getAssetAudioUrl(s3Assets.theifAudio), + singleAudio: getAssetAudioUrl(s3Assets.theifAudio), + }, + { + id: 86, + title: "Syllable", + syllable: "the", + word: "Father", + image: getAssetUrl(s3Assets.FatherImg), + audio: getAssetAudioUrl(s3Assets.FatherAudio), + singleAudio: getAssetAudioUrl(s3Assets.FatherAudio), + }, + ], + }, + { + syllable: "and", + items: [ + { + id: 87, + title: "Syllable", + syllable: "and", + word: "Hand", + image: getAssetUrl(s3Assets.HandImg), + audio: getAssetAudioUrl(s3Assets.HandAudio), + singleAudio: getAssetAudioUrl(s3Assets.HandAudio), + }, + { + id: 88, + title: "Syllable", + syllable: "and", + word: "Candle", + image: getAssetUrl(s3Assets.CandleImg), + audio: getAssetAudioUrl(s3Assets.CandleAudio), + singleAudio: getAssetAudioUrl(s3Assets.CandleAudio), + }, + ], + }, + { + syllable: "for", + items: [ + { + id: 89, + title: "Syllable", + syllable: "for", + word: "Forest", + image: getAssetUrl(s3Assets.ForestImg), + audio: getAssetAudioUrl(s3Assets.ForestAudio), + singleAudio: getAssetAudioUrl(s3Assets.ForestAudio), + }, + { + id: 90, + title: "Syllable", + syllable: "for", + word: "fort", + image: getAssetUrl(s3Assets.fortImg), + audio: getAssetAudioUrl(s3Assets.fortAudio), + singleAudio: getAssetAudioUrl(s3Assets.fortAudio), + }, + ], + }, + { + syllable: "hat", + items: [ + { + id: 91, + title: "Syllable", + syllable: "hat", + word: "hats", + image: getAssetUrl(s3Assets.hatsImg), + audio: getAssetAudioUrl(s3Assets.hatsAudio), + singleAudio: getAssetAudioUrl(s3Assets.hatsAudio), + }, + { + id: 92, + title: "Syllable", + syllable: "hat", + word: "Chatter", + image: getAssetUrl(s3Assets.ChatterImg), + audio: getAssetAudioUrl(s3Assets.ChatterAudio), + singleAudio: getAssetAudioUrl(s3Assets.ChatterAudio), + }, + ], + }, + { + syllable: "his", + items: [ + { + id: 93, + title: "Syllable", + syllable: "his", + word: "History", + image: getAssetUrl(s3Assets.HistoryImg), + audio: getAssetAudioUrl(s3Assets.HistoryAudio), + singleAudio: getAssetAudioUrl(s3Assets.HistoryAudio), + }, + { + id: 94, + title: "Syllable", + syllable: "his", + word: "Whisper", + image: getAssetUrl(s3Assets.WhisperImg), + audio: getAssetAudioUrl(s3Assets.WhisperAudio), + singleAudio: getAssetAudioUrl(s3Assets.WhisperAudio), + }, + ], + }, + { + syllable: "was", + items: [ + { + id: 95, + title: "Syllable", + syllable: "was", + word: "Wasp", + image: getAssetUrl(s3Assets.WaspImg), + audio: getAssetAudioUrl(s3Assets.WaspAudio), + singleAudio: getAssetAudioUrl(s3Assets.WaspAudio), + }, + { + id: 96, + title: "Syllable", + syllable: "was", + word: "Waste", + image: getAssetUrl(s3Assets.WasteImg), + audio: getAssetAudioUrl(s3Assets.WasteAudio), + singleAudio: getAssetAudioUrl(s3Assets.WasteAudio), + }, + ], + }, + { + syllable: "one", + items: [ + { + id: 97, + title: "Syllable", + syllable: "one", + word: "Bone", + image: getAssetUrl(s3Assets.BoneImg), + audio: getAssetAudioUrl(s3Assets.BoneAudio), + singleAudio: getAssetAudioUrl(s3Assets.BoneAudio), + }, + { + id: 98, + title: "Syllable", + syllable: "one", + word: "money", + image: getAssetUrl(s3Assets.moneyImg), + audio: getAssetAudioUrl(s3Assets.moneyAudio), + singleAudio: getAssetAudioUrl(s3Assets.moneyAudio), + }, + ], + }, + { + syllable: "our", + items: [ + { + id: 99, + title: "Syllable", + syllable: "our", + word: "colour", + image: getAssetUrl(s3Assets.colourImg), + audio: getAssetAudioUrl(s3Assets.colourAudio), + singleAudio: getAssetAudioUrl(s3Assets.colourAudio), + }, + { + id: 100, + title: "Syllable", + syllable: "our", + word: "four", + image: getAssetUrl(s3Assets.fourImg), + audio: getAssetAudioUrl(s3Assets.fourAudio), + singleAudio: getAssetAudioUrl(s3Assets.fourAudio), + }, + ], + }, + { + syllable: "wit", + items: [ + { + id: 101, + title: "Syllable", + syllable: "wit", + word: "witty", + image: getAssetUrl(s3Assets.wittyImg), + audio: getAssetAudioUrl(s3Assets.wittyAudio), + singleAudio: getAssetAudioUrl(s3Assets.wittyAudio), + }, + { + id: 102, + title: "Syllable", + syllable: "wit", + word: "Switch", + image: getAssetUrl(s3Assets.SwitchImg), + audio: getAssetAudioUrl(s3Assets.SwitchAudio), + singleAudio: getAssetAudioUrl(s3Assets.SwitchAudio), + }, + ], + }, + { + syllable: "not", + items: [ + { + id: 103, + title: "Syllable", + syllable: "not", + word: "notebook", + image: getAssetUrl(s3Assets.notebookImg), + audio: getAssetAudioUrl(s3Assets.notebookAudio), + singleAudio: getAssetAudioUrl(s3Assets.notebookAudio), + }, + { + id: 104, + title: "Syllable", + syllable: "not", + word: "note", + image: getAssetUrl(s3Assets.noteImg), + audio: getAssetAudioUrl(s3Assets.noteAudio), + singleAudio: getAssetAudioUrl(s3Assets.noteAudio), + }, + ], + }, + { + syllable: "Bed", + items: [ + { + id: 105, + title: "Syllable", + syllable: "Bed", + word: "Bedroom", + image: getAssetUrl(s3Assets.BedroomImg), + audio: getAssetAudioUrl(s3Assets.BedroomAudio), + singleAudio: getAssetAudioUrl(s3Assets.BedroomAudio), + }, + { + id: 106, + title: "Syllable", + syllable: "Bed", + word: "Bedtime", + image: getAssetUrl(s3Assets.BedtimeImg), + audio: getAssetAudioUrl(s3Assets.BedtimeAudio), + singleAudio: getAssetAudioUrl(s3Assets.BedtimeAudio), + }, + ], + }, + { + syllable: "hi", + items: [ + { + id: 107, + title: "Syllable", + syllable: "hi", + word: "Hindi", + image: getAssetUrl(s3Assets.HindiImg), + audio: getAssetAudioUrl(s3Assets.HindiAudio), + singleAudio: getAssetAudioUrl(s3Assets.HindiAudio), + }, + { + id: 108, + title: "Syllable", + syllable: "hi", + word: "child", + image: getAssetUrl(s3Assets.childImg), + audio: getAssetAudioUrl(s3Assets.childAudio), + singleAudio: getAssetAudioUrl(s3Assets.childAudio), + }, + ], + }, + { + letter: "E", + items: [ + { + id: 1, + title: "Vowel", + letters: "Ee", + letter: "e", + word: "Egg", + image: getAssetUrl(s3Assets.eggFiveImg), + audio: getAssetAudioUrl(s3Assets.EForEggAudio), + singleAudio: getAssetAudioUrl(s3Assets.EForEggAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.EForEggAudio), + }, + { + id: 2, + title: "Vowel", + letters: "Ee", + letter: "e", + word: "Pen", + image: getAssetUrl(s3Assets.penFourteenImg), + audio: getAssetAudioUrl(s3Assets.penPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.penPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.EForPenAudio), + }, + { + id: 3, + title: "Vowel", + letters: "Ee", + letter: "e", + word: "Kite", + image: getAssetUrl(s3Assets.kiteFiveImg), + audio: getAssetAudioUrl(s3Assets.kitePhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.kitePhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.EForKiteAudio), + }, + ], + }, + { + letter: "A", + items: [ + { + id: 4, + title: "Vowel", + letters: "Aa", + letter: "a", + word: "Apple", + image: getAssetUrl(s3Assets.appleOneImg), + audio: getAssetAudioUrl(s3Assets.AForAppleAudio), + singleAudio: getAssetAudioUrl(s3Assets.AForAppleAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.AForAppleAudio), + }, + { + id: 5, + title: "Vowel", + letters: "Aa", + letter: "a", + word: "Cat", + image: getAssetUrl(s3Assets.catOneImg), + audio: getAssetAudioUrl(s3Assets.catPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.catPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.AForCatAudio), + }, + { + id: 6, + title: "Vowel", + letters: "Aa", + letter: "a", + word: "Pea", + image: getAssetUrl(s3Assets.peaOneImg), + audio: getAssetAudioUrl(s3Assets.peaPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.peaPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.AForPeaAudio), + }, + ], + }, + { + letter: "O", + items: [ + { + id: 7, + title: "Vowel", + letters: "Oo", + letter: "o", + word: "Orange", + image: getAssetUrl(s3Assets.orangeFifteenImg), + audio: getAssetAudioUrl(s3Assets.OForOrangeAudio), + singleAudio: getAssetAudioUrl(s3Assets.OForOrangeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.OForOrangeAudio), + }, + { + id: 8, + title: "Vowel", + letters: "Oo", + letter: "o", + word: "Dog", + image: getAssetUrl(s3Assets.dogSevenImg), + audio: getAssetAudioUrl(s3Assets.dogPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.dogPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.OForDogAudio), + }, + { + id: 9, + title: "Vowel", + letters: "Oo", + letter: "o", + word: "Mango", + image: getAssetUrl(s3Assets.mangoThirteenImg), + audio: getAssetAudioUrl(s3Assets.mangoPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.mangoPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.OForMangoAudio), + }, + ], + }, + { + letter: "I", + items: [ + { + id: 10, + title: "Vowel", + letters: "Ii", + letter: "i", + word: "Ice", + image: getAssetUrl(s3Assets.iceThreeImg), + audio: getAssetAudioUrl(s3Assets.IForIceAudio), + singleAudio: getAssetAudioUrl(s3Assets.IForIceAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.IForIceAudio), + }, + { + id: 11, + title: "Vowel", + letters: "Ii", + letter: "i", + word: "Pig", + image: getAssetUrl(s3Assets.pigNineImg), + audio: getAssetAudioUrl(s3Assets.pigPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.pigPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.IForPigAudio), + }, + { + id: 12, + title: "Vowel", + letters: "Ii", + letter: "i", + word: "Chilly", + image: getAssetUrl(s3Assets.chilliImg), + audio: getAssetAudioUrl(s3Assets.chillyAud), + singleAudio: getAssetAudioUrl(s3Assets.chillyAud), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.IForChillyAudio), + }, + ], + }, + { + letter: "U", + items: [ + { + id: 13, + title: "Vowel", + letters: "Uu", + letter: "u", + word: "Under", + image: getAssetUrl(s3Assets.underImg), + audio: getAssetAudioUrl(s3Assets.underAudio), + singleAudio: getAssetAudioUrl(s3Assets.underAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.underAudio), + }, + { + id: 14, + title: "Vowel", + letters: "Uu", + letter: "u", + word: "Dust", + image: getAssetUrl(s3Assets.DustImg), + audio: getAssetAudioUrl(s3Assets.DustSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.DustSingleAudio), + }, + { + id: 15, + title: "Vowel", + letters: "Uu", + letter: "u", + word: "Laddu", + image: getAssetUrl(s3Assets.LadduTwentyOneImg), + audio: getAssetAudioUrl(s3Assets.ladduPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.ladduPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.UForLadduAudio), + }, + ], + }, + { + letter: "T", + items: [ + { + id: 16, + title: "Consonant", + letters: "Tt", + letter: "t", + word: "Tiger", + image: getAssetUrl(s3Assets.tigerSevenImg), + audio: getAssetAudioUrl(s3Assets.TForTigerAudio), + singleAudio: getAssetAudioUrl(s3Assets.TForTigerAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.TForTigerAudio), + }, + { + id: 17, + title: "Consonant", + letters: "Tt", + letter: "t", + word: "Watch", + image: getAssetUrl(s3Assets.watchTwentyImg), + audio: getAssetAudioUrl(s3Assets.watchPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.watchPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.TForWatchAudio), + }, + { + id: 18, + title: "Consonant", + letters: "Tt", + letter: "t", + word: "Plant", + image: getAssetUrl(s3Assets.plantTwentyImg), + audio: getAssetAudioUrl(s3Assets.plantPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.plantPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.TForPlantAudio), + }, + ], + }, + { + letter: "N", + items: [ + { + id: 19, + title: "Consonant", + letters: "Nn", + letter: "n", + word: "Nest", + image: getAssetUrl(s3Assets.NestFourteenImg), + audio: getAssetAudioUrl(s3Assets.NForNestAudio), + singleAudio: getAssetAudioUrl(s3Assets.NForNestAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.NForNestAudio), + }, + { + id: 20, + title: "Consonant", + letters: "Nn", + letter: "n", + word: "Honey", + image: getAssetUrl(s3Assets.HoneyFourteenImg), + audio: getAssetAudioUrl(s3Assets.honeyPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.honeyPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.NForHoneyAudio), + }, + { + id: 21, + title: "Consonant", + letters: "Nn", + letter: "n", + word: "Pen", + image: getAssetUrl(s3Assets.penFiveImg), + audio: getAssetAudioUrl(s3Assets.penPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.penPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.NForPenAudio), + }, + ], + }, + { + letter: "S", + items: [ + { + id: 22, + title: "Consonant", + letters: "Ss", + letter: "s", + word: "Sun", + image: getAssetUrl(s3Assets.sunNineteenImg), + audio: getAssetAudioUrl(s3Assets.SForSunAudio), + singleAudio: getAssetAudioUrl(s3Assets.SForSunAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.SForSunAudio), + }, + { + id: 23, + title: "Consonant", + letters: "Ss", + letter: "s", + word: "Horse", + image: getAssetUrl(s3Assets.horseNineteenImg), + audio: getAssetAudioUrl(s3Assets.horsePhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.horsePhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.SForHorseAudio), + }, + { + id: 24, + title: "Consonant", + letters: "Ss", + letter: "s", + word: "Bus", + image: getAssetUrl(s3Assets.busNineteenImg), + audio: getAssetAudioUrl(s3Assets.busPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.busPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.SForBusAudio), + }, + ], + }, + { + letter: "R", + items: [ + { + id: 25, + title: "Consonant", + letters: "Rr", + letter: "r", + word: "Rat", + image: getAssetUrl(s3Assets.ratEighteenImg), + audio: getAssetAudioUrl(s3Assets.RForRatAudio), + singleAudio: getAssetAudioUrl(s3Assets.RForRatAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.RForRatAudio), + }, + { + id: 26, + title: "Consonant", + letters: "Rr", + letter: "r", + word: "Carrot", + image: getAssetUrl(s3Assets.carrotEighteenImg), + audio: getAssetAudioUrl(s3Assets.carrotPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.carrotPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.RForCarrotAudio), + }, + { + id: 27, + title: "Consonant", + letters: "Rr", + letter: "r", + word: "Car", + image: getAssetUrl(s3Assets.carEighteenImg), + audio: getAssetAudioUrl(s3Assets.carPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.carPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.RForCarAudio), + }, + ], + }, + { + letter: "H", + items: [ + { + id: 28, + title: "Consonant", + letters: "Hh", + letter: "h", + word: "Hand", + image: getAssetUrl(s3Assets.handEightImg), + audio: getAssetAudioUrl(s3Assets.HForHandAudio), + singleAudio: getAssetAudioUrl(s3Assets.HForHandAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.HForHandAudio), + }, + { + id: 29, + title: "Consonant", + letters: "Hh", + letter: "h", + word: "Teacher", + image: getAssetUrl(s3Assets.teacherEightImg), + audio: getAssetAudioUrl(s3Assets.teacherPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.teacherPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.HForTeacherAudio), + }, + { + id: 30, + title: "Consonant", + letters: "Hh", + letter: "h", + word: "Earth", + image: getAssetUrl(s3Assets.earthEightImg), + audio: getAssetAudioUrl(s3Assets.earthPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.earthPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.HForEarthAudio), + }, + ], + }, + { + letter: "L", + items: [ + { + id: 31, + title: "Consonant", + letters: "Ll", + letter: "l", + word: "Lion", + image: getAssetUrl(s3Assets.LionTwelveImg), + audio: getAssetAudioUrl(s3Assets.LForLionAudio), + singleAudio: getAssetAudioUrl(s3Assets.LForLionAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.LForLionAudio), + }, + { + id: 32, + title: "Consonant", + letters: "Ll", + letter: "l", + word: "Balloon", + image: getAssetUrl(s3Assets.ballTwoImg), + audio: getAssetAudioUrl(s3Assets.balloonPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.balloonPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.LForBalloonAudio), + }, + { + id: 33, + title: "Consonant", + letters: "Ll", + letter: "l", + word: "Bell", + image: getAssetUrl(s3Assets.bellTwelveImg), + audio: getAssetAudioUrl(s3Assets.bellPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.bellPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.LForBellAudio), + }, + ], + }, + { + letter: "D", + items: [ + { + id: 34, + title: "Consonant", + letters: "Dd", + letter: "d", + word: "Dog", + image: getAssetUrl(s3Assets.dogFourImg), + audio: getAssetAudioUrl(s3Assets.DForDogAudio), + singleAudio: getAssetAudioUrl(s3Assets.DForDogAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.DForDogAudio), + }, + { + id: 35, + title: "Consonant", + letters: "Dd", + letter: "d", + word: "Lady", + image: getAssetUrl(s3Assets.LadyImg), + audio: getAssetAudioUrl(s3Assets.LadySingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.LadySingleAudio), + }, + { + id: 36, + title: "Consonant", + letters: "Dd", + letter: "d", + word: "Sad", + image: getAssetUrl(s3Assets.SadImg), + audio: getAssetAudioUrl(s3Assets.SadSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.SadSingleAudio), + }, + ], + }, + { + letter: "C", + items: [ + { + id: 37, + title: "Consonant", + letters: "Cc", + letter: "c", + word: "Cat", + image: getAssetUrl(s3Assets.catOneImg), + audio: getAssetAudioUrl(s3Assets.CForCatAudio), + singleAudio: getAssetAudioUrl(s3Assets.CForCatAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.CForCatAudio), + }, + { + id: 38, + title: "Consonant", + letters: "Cc", + letter: "c", + word: "Ice", + image: getAssetUrl(s3Assets.iceThreeImg), + audio: getAssetAudioUrl(s3Assets.icePhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.icePhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.CForIceAudio), + }, + { + id: 39, + title: "Consonant", + letters: "Cc", + letter: "c", + word: "Garlic", + image: getAssetUrl(s3Assets.garlicThreeImg), + audio: getAssetAudioUrl(s3Assets.garlicPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.garlicPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.CForGarlicAudio), + }, + ], + }, + { + letter: "M", + items: [ + { + id: 40, + title: "Consonant", + letters: "Mm", + letter: "m", + word: "Mango", + image: getAssetUrl(s3Assets.mangoThirteenImg), + audio: getAssetAudioUrl(s3Assets.MForMangoAudio), + singleAudio: getAssetAudioUrl(s3Assets.MForMangoAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.MForMangoAudio), + }, + { + id: 41, + title: "Consonant", + letters: "Mm", + letter: "m", + word: "Lemon", + image: getAssetUrl(s3Assets.lemonThirteenImg), + audio: getAssetAudioUrl(s3Assets.lemonPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.lemonPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.MForLemonAudio), + }, + { + id: 42, + title: "Consonant", + letters: "Mm", + letter: "m", + word: "Jam", + image: getAssetUrl(s3Assets.jamTenImg), + audio: getAssetAudioUrl(s3Assets.jamPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.jamPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.MForJamAudio), + }, + ], + }, + { + letter: "F", + items: [ + { + id: 43, + title: "Consonant", + letters: "Ff", + letter: "f", + word: "Fish", + image: getAssetUrl(s3Assets.fishSixImg), + audio: getAssetAudioUrl(s3Assets.FForFishAudio), + singleAudio: getAssetAudioUrl(s3Assets.FForFishAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.FForFishAudio), + }, + { + id: 44, + title: "Consonant", + letters: "Ff", + letter: "f", + word: "Giraffe", + image: getAssetUrl(s3Assets.girraffeSixImg), + audio: getAssetAudioUrl(s3Assets.giraffePhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.giraffePhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.FForGiraffeAudio), + }, + { + id: 45, + title: "Consonant", + letters: "Ff", + letter: "f", + word: "Leaf", + image: getAssetUrl(s3Assets.LeafSixImg), + audio: getAssetAudioUrl(s3Assets.leafPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.leafPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.FForLeafAudio), + }, + ], + }, + { + letter: "Y", + items: [ + { + id: 46, + title: "Consonant", + letters: "Yy", + letter: "y", + word: "Yak", + image: getAssetUrl(s3Assets.yakTwentyFiveImg), + audio: getAssetAudioUrl(s3Assets.YForYakAudio), + singleAudio: getAssetAudioUrl(s3Assets.YForYakAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.YForYakAudio), + }, + { + id: 47, + title: "Consonant", + letters: "Yy", + letter: "y", + word: "Papaya", + image: getAssetUrl(s3Assets.papayaTwentyFiveImg), + audio: getAssetAudioUrl(s3Assets.papayaPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.papayaPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.YForPapayaAudio), + }, + { + id: 48, + title: "Consonant", + letters: "Yy", + letter: "y", + word: "Key", + image: getAssetUrl(s3Assets.KeyTwentyFiveImg), + audio: getAssetAudioUrl(s3Assets.keyPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.keyPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.YForKeyAudio), + }, + ], + }, + { + letter: "W", + items: [ + { + id: 49, + title: "Consonant", + letters: "Ww", + letter: "w", + word: "Wind", + image: getAssetUrl(s3Assets.WindImg), + audio: getAssetAudioUrl(s3Assets.WindAudio), + singleAudio: getAssetAudioUrl(s3Assets.WindAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.WindAudio), + }, + { + id: 50, + title: "Consonant", + letters: "Ww", + letter: "w", + word: "Sweet", + image: getAssetUrl(s3Assets.SweetImg), + audio: getAssetAudioUrl(s3Assets.SweetSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.SweetSingleAudio), + }, + { + id: 51, + title: "Consonant", + letters: "Ww", + letter: "w", + word: "Crow", + image: getAssetUrl(s3Assets.CrowTwentyThreeImg), + audio: getAssetAudioUrl(s3Assets.crowPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.crowPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.WForCrowAudio), + }, + ], + }, + { + letter: "G", + items: [ + { + id: 52, + title: "Consonant", + letters: "Gg", + letter: "g", + word: "Goat", + image: getAssetUrl(s3Assets.goatSevenImg), + audio: getAssetAudioUrl(s3Assets.GForGoatAudio), + singleAudio: getAssetAudioUrl(s3Assets.GForGoatAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.GForGoatAudio), + }, + { + id: 53, + title: "Consonant", + letters: "Gg", + letter: "g", + word: "Tiger", + image: getAssetUrl(s3Assets.tigerSevenImg), + audio: getAssetAudioUrl(s3Assets.tigerPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.tigerPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.GForTigerAudio), + }, + { + id: 54, + title: "Consonant", + letters: "Gg", + letter: "g", + word: "Dog", + image: getAssetUrl(s3Assets.dogFourImg), + audio: getAssetAudioUrl(s3Assets.dogPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.dogPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.GForDogAudio), + }, + ], + }, + { + letter: "P", + items: [ + { + id: 55, + title: "Consonant", + letters: "Pp", + letter: "p", + word: "Pen", + image: getAssetUrl(s3Assets.penFiveImg), + audio: getAssetAudioUrl(s3Assets.PForPenAudio), + singleAudio: getAssetAudioUrl(s3Assets.PForPenAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.PForPenAudio), + }, + { + id: 56, + title: "Consonant", + letters: "Pp", + letter: "p", + word: "Apple", + image: getAssetUrl(s3Assets.appleOneImg), + audio: getAssetAudioUrl(s3Assets.applePhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.applePhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.PForAppleAudio), + }, + { + id: 57, + title: "Consonant", + letters: "Pp", + letter: "p", + word: "Cap", + image: getAssetUrl(s3Assets.capSixteenImg), + audio: getAssetAudioUrl(s3Assets.capPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.capPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.PForCapAudio), + }, + ], + }, + { + letter: "B", + items: [ + { + id: 58, + title: "Consonant", + letters: "Bb", + letter: "b", + word: "Ball", + image: getAssetUrl(s3Assets.ballGif), + audio: getAssetAudioUrl(s3Assets.BForBallAudio), + singleAudio: getAssetAudioUrl(s3Assets.BForBallAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.BForBallAudio), + }, + { + id: 59, + title: "Consonant", + letters: "Bb", + letter: "b", + word: "Zebra", + image: getAssetUrl(s3Assets.zebraTwentySixImg), + audio: getAssetAudioUrl(s3Assets.zebraPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.zebraPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.BForZebraAudio), + }, + { + id: 60, + title: "Consonant", + letters: "Bb", + letter: "b", + word: "Cub", + image: getAssetUrl(s3Assets.cubTwoImg), + audio: getAssetAudioUrl(s3Assets.cubPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.cubPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.BForCubAudio), + }, + ], + }, + { + letter: "V", + items: [ + { + id: 61, + title: "Consonant", + letters: "Vv", + letter: "v", + word: "Van", + image: getAssetUrl(s3Assets.VanTwentyTwoImg), + audio: getAssetAudioUrl(s3Assets.VForVanAudio), + singleAudio: getAssetAudioUrl(s3Assets.VForVanAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.VForVanAudio), + }, + { + id: 62, + title: "Consonant", + letters: "Vv", + letter: "v", + word: "Guava", + image: getAssetUrl(s3Assets.GuavaTwentyTwoImg), + audio: getAssetAudioUrl(s3Assets.guavaPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.guavaPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.VForGuavaAudio), + }, + ], + }, + { + letter: "K", + items: [ + { + id: 64, + title: "Consonant", + letters: "Kk", + letter: "k", + word: "Kite", + image: getAssetUrl(s3Assets.kiteFiveImg), + audio: getAssetAudioUrl(s3Assets.KForKiteAudio), + singleAudio: getAssetAudioUrl(s3Assets.KForKiteAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.KForKiteAudio), + }, + { + id: 65, + title: "Consonant", + letters: "Kk", + letter: "k", + word: "Monkey", + image: getAssetUrl(s3Assets.monkeyElevenImg), + audio: getAssetAudioUrl(s3Assets.monkeyPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.monkeyPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.KForMonkeyAudio), + }, + { + id: 66, + title: "Consonant", + letters: "Kk", + letter: "k", + word: "Book", + image: getAssetUrl(s3Assets.bookElevenImg), + audio: getAssetAudioUrl(s3Assets.bookPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.bookPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.KForBookAudio), + }, + ], + }, + { + letter: "J", + items: [ + { + id: 67, + title: "Consonant", + letters: "Jj", + letter: "j", + word: "Jam", + image: getAssetUrl(s3Assets.jamTenImg), + audio: getAssetAudioUrl(s3Assets.JForJamAudio), + singleAudio: getAssetAudioUrl(s3Assets.JForJamAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.JForJamAudio), + }, + { + id: 68, + title: "Consonant", + letters: "Jj", + letter: "j", + word: "brinjal", + image: getAssetUrl(s3Assets.brinjalTenImg), + audio: getAssetAudioUrl(s3Assets.brinjalPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.brinjalPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.JForBrinjalAudio), + }, + ], + }, + { + letter: "X", + items: [ + { + id: 70, + title: "Consonant", + letters: "Xx", + letter: "x", + word: "Xray", + image: getAssetUrl(s3Assets.xrayTwentyFourImg), + audio: getAssetAudioUrl(s3Assets.XForXrayAudio), + singleAudio: getAssetAudioUrl(s3Assets.XForXrayAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.XForXrayAudio), + }, + { + id: 71, + title: "Consonant", + letters: "Xx", + letter: "x", + word: "Textbook", + image: getAssetUrl(s3Assets.bookElevenImg), + audio: getAssetAudioUrl(s3Assets.textbookPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.textbookPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.XForTextbookAudio), + }, + { + id: 72, + title: "Consonant", + letters: "Xx", + letter: "x", + word: "Fox", + image: getAssetUrl(s3Assets.foxTwentyFourImg), + audio: getAssetAudioUrl(s3Assets.foxPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.foxPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.XForFoxAudio), + }, + ], + }, + { + letter: "Q", + items: [ + { + id: 73, + title: "Consonant", + letters: "Qq", + letter: "q", + word: "Queen", + image: getAssetUrl(s3Assets.queenSixteenImg), + audio: getAssetAudioUrl(s3Assets.QForQueenAudio), + singleAudio: getAssetAudioUrl(s3Assets.QForQueenAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.QForQueenAudio), + }, + { + id: 74, + title: "Consonant", + letters: "Qq", + letter: "q", + word: "Equal", + image: getAssetUrl(s3Assets.EqualImg), + audio: getAssetAudioUrl(s3Assets.EqualSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.EqualSingleAudio), + }, + ], + }, + { + letter: "Z", + items: [ + { + id: 76, + title: "Consonant", + letters: "Zz", + letter: "z", + word: "Zebra", + image: getAssetUrl(s3Assets.zebraTwentySixImg), + audio: getAssetAudioUrl(s3Assets.ZForZebraAudio), + singleAudio: getAssetAudioUrl(s3Assets.ZForZebraAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ZForZebraAudio), + }, + { + id: 77, + title: "Consonant", + letters: "Zz", + letter: "z", + word: "Puzzle", + image: getAssetUrl(s3Assets.PuzzleTwentySixImg), + audio: getAssetAudioUrl(s3Assets.puzzlePhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.puzzlePhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ZForPuzzleAudio), + }, + { + id: 78, + title: "Consonant", + letters: "Zz", + letter: "z", + word: "Quiz", + image: getAssetUrl(s3Assets.PuzzleTwentySixImg), + audio: getAssetAudioUrl(s3Assets.quizPhonemeAudio), + singleAudio: getAssetAudioUrl(s3Assets.quizPhonemeAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ZForQuizAudio), + }, + ], + }, +]; + +export const dataKn = [ + { + letter: "ರ", + items: [ + { + id: 1, + title: "Letter", + letter: "ರ", + word: "ರಸ", + image: getAssetUrl(s3Assets.ರಸImg), + audio: getAssetAudioUrl(s3Assets.ರಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ರಸAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ರಸAudio), + }, + { + id: 2, + title: "Letter", + letter: "ರ", + word: "ಅರಸ", + image: getAssetUrl(s3Assets.ಅರಸImg), + audio: getAssetAudioUrl(s3Assets.ಅರಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಅರಸAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಅರಸAudio), + }, + { + id: 3, + title: "Letter", + letter: "ರ", + word: "ದಸರ", + image: getAssetUrl(s3Assets.ದಸರImg), + audio: getAssetAudioUrl(s3Assets.ದಸರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ದಸರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ದಸರAudio), + }, + ], + }, + { + letter: "ಗ", + items: [ + { + id: 4, + title: "Letter", + letter: "ಗ", + word: "ಗರಿ", + image: getAssetUrl(s3Assets.ಗರImg), + audio: getAssetAudioUrl(s3Assets.ಗರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಗರAudio), + }, + { + id: 5, + title: "Letter", + letter: "ಗ", + word: "ಆಗಸ", + image: getAssetUrl(s3Assets.ಆಗಸImg), + audio: getAssetAudioUrl(s3Assets.ಆಗಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಗಸAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಆಗಸAudio), + }, + { + id: 6, + title: "Letter", + letter: "ಗ", + word: "ಉರಗ", + image: getAssetUrl(s3Assets.ಉರಗImg), + audio: getAssetAudioUrl(s3Assets.ಉರಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಉರಗAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಉರಗAudio), + }, + ], + }, + { + letter: "ಸ", + items: [ + { + id: 7, + title: "Letter", + letter: "ಸ", + word: "ಸರ", + image: getAssetUrl(s3Assets.ಸರImg), + audio: getAssetAudioUrl(s3Assets.ಸರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಸರAudio), + }, + { + id: 8, + title: "Letter", + letter: "ಸ", + word: "ಆಸನ", + image: getAssetUrl(s3Assets.ಆಸನImg), + audio: getAssetAudioUrl(s3Assets.ಆಸನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಸನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಆಸನAudio), + }, + { + id: 9, + title: "Letter", + letter: "ಸ", + word: "ರಸ", + image: getAssetUrl(s3Assets.ರಸ2Img), + audio: getAssetAudioUrl(s3Assets.ರಸ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ರಸ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ರಸ2Audio), + }, + ], + }, + { + letter: "ದ", + items: [ + { + id: 10, + title: "Letter", + letter: "ದ", + word: "ದಸರ", + image: getAssetUrl(s3Assets.ದಸರ2Img), + audio: getAssetAudioUrl(s3Assets.ದಸರ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ದಸರ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ದಸರ2Audio), + }, + { + id: 11, + title: "Letter", + letter: "ದ", + word: "ಉದಯ", + image: getAssetUrl(s3Assets.ಉದಯImg), + audio: getAssetAudioUrl(s3Assets.ಉದಯAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಉದಯAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಉದಯAudio), + }, + { + id: 12, + title: "Letter", + letter: "ದ", + word: "ಕದ", + image: getAssetUrl(s3Assets.ಕದImg), + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕದAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕದAudio), + }, + ], + }, + { + letter: "ಅ", + items: [ + { + id: 13, + title: "Letter", + letter: "ಅ", + word: "ಅರಸ", + image: getAssetUrl(s3Assets.ಅರಸ2Img), + audio: getAssetAudioUrl(s3Assets.ಅರಸ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಅರಸ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಅರಸ2Audio), + }, + ], + }, + { + letter: "ಜ", + items: [ + { + id: 14, + title: "Letter", + letter: "ಜ", + word: "ಜನ", + image: getAssetUrl(s3Assets.ಜನImg), + audio: getAssetAudioUrl(s3Assets.ಜನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಜನAudio), + }, + { + id: 15, + title: "Letter", + letter: "ಜ", + word: "ರಜತ", + image: getAssetUrl(s3Assets.ರಜತImg), + audio: getAssetAudioUrl(s3Assets.ರಜತAudio), + singleAudio: getAssetAudioUrl(s3Assets.ರಜತAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ರಜತAudio), + }, + { + id: 16, + title: "Letter", + letter: "ಜ", + word: "ಗಜ", + image: getAssetUrl(s3Assets.ಗಜImg), + audio: getAssetAudioUrl(s3Assets.ಗಜAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಜAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಗಜAudio), + }, + ], + }, + { + letter: "ವ", + items: [ + { + id: 17, + title: "Letter", + letter: "ವ", + word: "ವನ", + image: getAssetUrl(s3Assets.ವನImg), + audio: getAssetAudioUrl(s3Assets.ವನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ವನAudio), + }, + { + id: 18, + title: "Letter", + letter: "ವ", + word: "ದವಸ", + image: getAssetUrl(s3Assets.ದವಸImg), + audio: getAssetAudioUrl(s3Assets.ದವಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ದವಸAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ದವಸAudio), + }, + { + id: 19, + title: "Letter", + letter: "ವ", + word: "ಬಸವ", + image: getAssetUrl(s3Assets.ಬಸವImg), + audio: getAssetAudioUrl(s3Assets.ಬಸವAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಬಸವAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಬಸವAudio), + }, + ], + }, + { + letter: "ಮ", + items: [ + { + id: 20, + title: "Letter", + letter: "ಮ", + word: "ಮರ", + image: getAssetUrl(s3Assets.ಮರImg), + audio: getAssetAudioUrl(s3Assets.ಮರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಮರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಮರAudio), + }, + { + id: 21, + title: "Letter", + letter: "ಮ", + word: "ಕಮಲ", + image: getAssetUrl(s3Assets.ಕಮಲImg), + audio: getAssetAudioUrl(s3Assets.ಕಮಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಮಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಮಲAudio), + }, + { + id: 22, + title: "Letter", + letter: "ಮ", + word: "ಸಮ", + image: getAssetUrl(s3Assets.ಸಮImg), + audio: getAssetAudioUrl(s3Assets.ಸಮAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸಮAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಸಮAudio), + }, + ], + }, + { + letter: "ಬ", + items: [ + { + id: 23, + title: "Letter", + letter: "ಬ", + word: "ಬನ", + image: getAssetUrl(s3Assets.ಬನImg), + audio: getAssetAudioUrl(s3Assets.ಬನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಬನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಬನAudio), + }, + { + id: 24, + title: "Letter", + letter: "ಬ", + word: "ತಬಲ", + image: getAssetUrl(s3Assets.ತಬಲImg), + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + }, + { + id: 25, + title: "Letter", + letter: "ಬ", + word: "ಕಂಬ", + image: getAssetUrl(s3Assets.ಕಬImg), + audio: getAssetAudioUrl(s3Assets.ಕಬAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಬAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಬAudio), + }, + ], + }, + { + letter: "ನ", + items: [ + { + id: 26, + title: "Letter", + letter: "ನ", + word: "ನಗ", + image: getAssetUrl(s3Assets.ನಗImg), + audio: getAssetAudioUrl(s3Assets.ನಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ನಗAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ನಗAudio), + }, + { + id: 27, + title: "Letter", + letter: "ನ", + word: "ವನಜ", + image: getAssetUrl(s3Assets.ವನಜImg), + audio: getAssetAudioUrl(s3Assets.ವನಜAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವನಜAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ವನಜAudio), + }, + { + id: 28, + title: "Letter", + letter: "ನ", + word: "ವಾಚನ", + image: getAssetUrl(s3Assets.ವಚನImg), + audio: getAssetAudioUrl(s3Assets.ವಚನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವಚನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ವಚನAudio), + }, + ], + }, + { + letter: "ಪ", + items: [ + { + id: 29, + title: "Letter", + letter: "ಪ", + word: "ಪದ", + image: getAssetUrl(s3Assets.ಪದImg), + audio: getAssetAudioUrl(s3Assets.ಪದAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪದAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಪದAudio), + }, + { + id: 30, + title: "Letter", + letter: "ಪ", + word: "ಟಪಟಪ", + image: getAssetUrl(s3Assets.ಟಪಟಪImg), + audio: getAssetAudioUrl(s3Assets.ಟಪಟಪAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಟಪಟಪAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಟಪಟಪAudio), + }, + { + id: 31, + title: "Letter", + letter: "ಪ", + word: "ಜಪ", + image: getAssetUrl(s3Assets.ಜಪImg), + audio: getAssetAudioUrl(s3Assets.ಜಪAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜಪAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಜಪAudio), + }, + ], + }, + { + letter: "ಯ", + items: [ + { + id: 32, + title: "Letter", + letter: "ಯ", + word: "ಯಮ", + image: getAssetUrl(s3Assets.ಯಮImg), + audio: getAssetAudioUrl(s3Assets.ಯಮAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಯಮAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಯಮAudio), + }, + { + id: 33, + title: "Letter", + letter: "ಯ", + word: "ನಯನ", + image: getAssetUrl(s3Assets.ನಯನ2Img), + audio: getAssetAudioUrl(s3Assets.ನಯನ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ನಯನ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ನಯನ2Audio), + }, + { + id: 34, + title: "Letter", + letter: "ಯ", + word: "ಜಯ", + image: getAssetUrl(s3Assets.ಜಯImg), + audio: getAssetAudioUrl(s3Assets.ಜಯAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜಯAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಜಯAudio), + }, + ], + }, + { + letter: "ಉ", + items: [ + { + id: 35, + title: "Letter", + letter: "ಉ", + word: "ಉದಯ", + image: getAssetUrl(s3Assets.ಉದಯ2Img), + audio: getAssetAudioUrl(s3Assets.ಉದಯ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಉದಯ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಉದಯ2Audio), + }, + ], + }, + { + letter: "ಡ", + items: [ + { + id: 36, + title: "Letter", + letter: "ಡ", + word: "ಡಮರ", + image: getAssetUrl(s3Assets.ಡಮರImg), + audio: getAssetAudioUrl(s3Assets.ಡಮರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಡಮರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಡಮರAudio), + }, + { + id: 37, + title: "Letter", + letter: "ಡ", + word: "ಕಡಗ", + image: getAssetUrl(s3Assets.ಕಡಗImg), + audio: getAssetAudioUrl(s3Assets.ಕಡಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಡಗAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಡಗAudio), + }, + { + id: 38, + title: "Letter", + letter: "ಡ", + word: "ಉಡ", + image: getAssetUrl(s3Assets.ಉಡImg), + audio: getAssetAudioUrl(s3Assets.ಉಡAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಉಡAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಉಡAudio), + }, + ], + }, + { + letter: "ಟ", + items: [ + { + id: 39, + title: "Letter", + letter: "ಟ", + word: "ಟಪಟಪ", + image: getAssetUrl(s3Assets.ಟಪಟಪ2Img), + audio: getAssetAudioUrl(s3Assets.ಟಪಟಪ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಟಪಟಪ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಟಪಟಪ2Audio), + }, + { + id: 40, + title: "Letter", + letter: "ಟ", + word: "ನಾಟಕ", + image: getAssetUrl(s3Assets.ನಟಕImg), + audio: getAssetAudioUrl(s3Assets.ನಟಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ನಟಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ನಟಕAudio), + }, + { + id: 41, + title: "Letter", + letter: "ಟ", + word: "ಆಟ", + image: getAssetUrl(s3Assets.ಆಟImg), + audio: getAssetAudioUrl(s3Assets.ಆಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಟAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಆಟAudio), + }, + ], + }, + { + letter: "ಚ", + items: [ + { + id: 42, + title: "Letter", + letter: "ಚ", + word: "ಚಮಚ", + image: getAssetUrl(s3Assets.ಚಮಚImg), + audio: getAssetAudioUrl(s3Assets.ಚಮಚAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಚಮಚAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಚಮಚAudio), + }, + { + id: 43, + title: "Letter", + letter: "ಚ", + word: "ಈಚಲ", + image: getAssetUrl(s3Assets.ಈಚಲImg), + audio: getAssetAudioUrl(s3Assets.ಈಚಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಈಚಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಈಚಲAudio), + }, + { + id: 44, + title: "Letter", + letter: "ಚ", + word: "ಮಂಚ", + image: getAssetUrl(s3Assets.ಮಚImg), + audio: getAssetAudioUrl(s3Assets.ಮಚAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಮಚAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಮಚAudio), + }, + ], + }, + { + letter: "ಲ", + items: [ + { + id: 45, + title: "Letter", + letter: "ಲ", + word: "ಲವಣ", + image: getAssetUrl(s3Assets.ಲವಣImg), + audio: getAssetAudioUrl(s3Assets.ಲವಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಲವಣAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಲವಣAudio), + }, + { + id: 46, + title: "Letter", + letter: "ಲ", + word: "ಕಲರವ", + image: getAssetUrl(s3Assets.ಕಲರವImg), + audio: getAssetAudioUrl(s3Assets.ಕಲರವAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಲರವAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಲರವAudio), + }, + { + id: 47, + title: "Letter", + letter: "ಲ", + word: "ಬಲ", + image: getAssetUrl(s3Assets.ಬಲImg), + audio: getAssetAudioUrl(s3Assets.ಬಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಬಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಬಲAudio), + }, + ], + }, + { + letter: "ಈ", + items: [ + { + id: 48, + title: "Letter", + letter: "ಈ", + word: "ಈಚಲ", + image: getAssetUrl(s3Assets.ಈಚಲ2Img), + audio: getAssetAudioUrl(s3Assets.ಈಚಲ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಈಚಲ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಈಚಲ2Audio), + }, + ], + }, + { + letter: "ಊ", + items: [ + { + id: 49, + title: "Letter", + letter: "ಊ", + word: "ಊಟ", + image: getAssetUrl(s3Assets.ಊಟImg), + audio: getAssetAudioUrl(s3Assets.ಊಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಊಟAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಊಟAudio), + }, + ], + }, + { + letter: "ಕ", + items: [ + { + id: 50, + title: "Letter", + letter: "ಕ", + word: "ಕದ", + image: getAssetUrl(s3Assets.ಕದ2Img), + audio: getAssetAudioUrl(s3Assets.ಕದ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಕದ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕದ2Audio), + }, + { + id: 51, + title: "Letter", + letter: "ಕ", + word: "ಟಕಟಕ", + image: getAssetUrl(s3Assets.ಟಕಟಕImg), + audio: getAssetAudioUrl(s3Assets.ಟಕಟಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಟಕಟಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಟಕಟಕAudio), + }, + { + id: 52, + title: "Letter", + letter: "ಕ", + word: "ಪದಕ", + image: getAssetUrl(s3Assets.ಪದಕImg), + audio: getAssetAudioUrl(s3Assets.ಪದಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪದಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಪದಕAudio), + }, + ], + }, + { + letter: "ಎ", + items: [ + { + id: 53, + title: "Letter", + letter: "ಎ", + word: "ಎರಕ", + image: getAssetUrl(s3Assets.ಎರಕImg), + audio: getAssetAudioUrl(s3Assets.ಎರಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಎರಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಎರಕAudio), + }, + ], + }, + { + letter: "ಏ", + items: [ + { + id: 54, + title: "Letter", + letter: "ಏ", + word: "ಏತ", + image: getAssetUrl(s3Assets.ಏತImg), + audio: getAssetAudioUrl(s3Assets.ಏತAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಏತAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಏತAudio), + }, + ], + }, + { + letter: "ಇ", + items: [ + { + id: 55, + title: "Letter", + letter: "ಇ", + word: "ಇಲಿ", + image: getAssetUrl(s3Assets.ಇಲImg), + audio: getAssetAudioUrl(s3Assets.ಇಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಇಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಇಲAudio), + }, + ], + }, + { + letter: "ಆ", + items: [ + { + id: 56, + title: "Letter", + letter: "ಆ", + word: "ಆಲ", + image: getAssetUrl(s3Assets.ಆಲImg), + audio: getAssetAudioUrl(s3Assets.ಆಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಆಲAudio), + }, + ], + }, + { + letter: "ತ", + items: [ + { + id: 57, + title: "Letter", + letter: "ತ", + word: "ತಬಲ", + image: getAssetUrl(s3Assets.ತಬಲ2Img), + audio: getAssetAudioUrl(s3Assets.ತಬಲ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ತಬಲ2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ತಬಲ2Audio), + }, + { + id: 58, + title: "Letter", + letter: "ತ", + word: "ಔತಣ", + image: getAssetUrl(s3Assets.ಔತಣImg), + audio: getAssetAudioUrl(s3Assets.ಔತಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಔತಣAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಔತಣAudio), + }, + { + id: 59, + title: "Letter", + letter: "ತ", + word: "ಊತ", + image: getAssetUrl(s3Assets.ಊತImg), + audio: getAssetAudioUrl(s3Assets.ಊತAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಊತAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಊತAudio), + }, + ], + }, + { + letter: "ಳ", + items: [ + { + id: 60, + title: "Letter", + letter: "ಳ", + word: "ಜಳಕ", + image: getAssetUrl(s3Assets.ಜಳಕImg), + audio: getAssetAudioUrl(s3Assets.ಜಳಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜಳಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಜಳಕAudio), + }, + { + id: 61, + title: "Letter", + letter: "ಳ", + word: "ನಳ", + image: getAssetUrl(s3Assets.ನಳImg), + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ನಳAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ನಳAudio), + }, + ], + }, + { + letter: "ಓ", + items: [ + { + id: 62, + title: "Letter", + letter: "ಓ", + word: "ಓಟ", + image: getAssetUrl(s3Assets.ಓಟImg), + audio: getAssetAudioUrl(s3Assets.ಓಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಓಟAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಓಟAudio), + }, + ], + }, + { + letter: "ಔ", + items: [ + { + id: 63, + title: "Letter", + letter: "ಔ", + word: "ಔಡಲ", + image: getAssetUrl(s3Assets.ಔಡಲImg), + audio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + }, + ], + }, + { + letter: "ಹ", + items: [ + { + id: 64, + title: "Letter", + letter: "ಹ", + word: "ಹವಳ", + image: getAssetUrl(s3Assets.ಹವಳImg), + audio: getAssetAudioUrl(s3Assets.ಹವಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹವಳAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಹವಳAudio), + }, + { + id: 65, + title: "Letter", + letter: "ಹ", + word: "ವಾಹನ", + image: getAssetUrl(s3Assets.ವಹನImg), + audio: getAssetAudioUrl(s3Assets.ವಹನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವಹನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ವಹನAudio), + }, + { + id: 66, + title: "Letter", + letter: "ಹ", + word: "ಕಲಹ", + image: getAssetUrl(s3Assets.ಕಲಹImg), + audio: getAssetAudioUrl(s3Assets.ಕಲಹAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಲಹAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಲಹAudio), + }, + ], + }, + { + letter: "ಶ", + items: [ + { + id: 67, + title: "Letter", + letter: "ಶ", + word: "ಶರ", + image: getAssetUrl(s3Assets.ಶರImg), + audio: getAssetAudioUrl(s3Assets.ಶರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಶರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಶರAudio), + }, + { + id: 68, + title: "Letter", + letter: "ಶ", + word: "ದಶಕ", + image: getAssetUrl(s3Assets.ದಶಕImg), + audio: getAssetAudioUrl(s3Assets.ದಶಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ದಶಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ದಶಕAudio), + }, + { + id: 69, + title: "Letter", + letter: "ಶ", + word: "ಕಳಶ", + image: getAssetUrl(s3Assets.ಕಳಶImg), + audio: getAssetAudioUrl(s3Assets.ಕಳಶAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಳಶAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಳಶAudio), + }, + ], + }, + { + letter: "ಷ", + items: [ + { + id: 70, + title: "Letter", + letter: "ಷ", + word: "ಉಷ", + image: getAssetUrl(s3Assets.ಉಷImg), + audio: getAssetAudioUrl(s3Assets.ಉಷAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಉಷAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಉಷAudio), + }, + { + id: 71, + title: "Letter", + letter: "ಷ", + word: "ಔಷಧ", + image: getAssetUrl(s3Assets.ಔಷಧImg), + audio: getAssetAudioUrl(s3Assets.ಔಷಧAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಔಷಧAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಔಷಧAudio), + }, + ], + }, + { + letter: "ಐ", + items: [ + { + id: 72, + title: "Letter", + letter: "ಐ", + word: "ಐದಳ", + image: getAssetUrl(s3Assets.ಐದಳImg), + audio: getAssetAudioUrl(s3Assets.ಐದಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಐದಳAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಐದಳAudio), + }, + ], + }, + { + letter: "ಋ", + items: [ + { + id: 73, + title: "Letter", + letter: "ಋ", + word: "ಋಷಿ", + image: getAssetUrl(s3Assets.ಋಷImg), + audio: getAssetAudioUrl(s3Assets.ಋಷAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಋಷAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಋಷAudio), + }, + ], + }, + { + letter: "ಣ", + items: [ + { + id: 74, + title: "Letter", + letter: "ಣ", + word: "ಹಣತೆ", + image: getAssetUrl(s3Assets.ಹಣತImg), + audio: getAssetAudioUrl(s3Assets.ಹಣತAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಣತAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಹಣತAudio), + }, + { + id: 75, + title: "Letter", + letter: "ಣ", + word: "ಹಣ", + image: getAssetUrl(s3Assets.ಹಣImg), + audio: getAssetAudioUrl(s3Assets.ಹಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಣAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಹಣAudio), + }, + ], + }, + { + letter: "ಛ", + items: [ + { + id: 76, + title: "Letter", + letter: "ಛ", + word: "ಛತ್ರಿ", + image: getAssetUrl(s3Assets.ಛತರImg), + audio: getAssetAudioUrl(s3Assets.ಛತರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಛತರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಛತರAudio), + }, + ], + }, + { + letter: "ಒ", + items: [ + { + id: 77, + title: "Letter", + letter: "ಒ", + word: "ಒಣಮರ", + image: getAssetUrl(s3Assets.ಒಣಮರImg), + audio: getAssetAudioUrl(s3Assets.ಒಣಮರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಒಣಮರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಒಣಮರAudio), + }, + ], + }, + { + letter: "ಧ", + items: [ + { + id: 78, + title: "Letter", + letter: "ಧ", + word: "ಧನ", + image: getAssetUrl(s3Assets.ಧನImg), + audio: getAssetAudioUrl(s3Assets.ಧನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಧನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಧನAudio), + }, + { + id: 79, + title: "Letter", + letter: "ಧ", + word: "ಸಾಧನೆ", + image: getAssetUrl(s3Assets.ಸಧನImg), + audio: getAssetAudioUrl(s3Assets.ಸಧನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸಧನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಸಧನAudio), + }, + { + id: 80, + title: "Letter", + letter: "ಧ", + word: "ಗಂಧ", + image: getAssetUrl(s3Assets.ಗಧImg), + audio: getAssetAudioUrl(s3Assets.ಗಧAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಧAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಗಧAudio), + }, + ], + }, + { + letter: "ಥ", + items: [ + { + id: 81, + title: "Letter", + letter: "ಥ", + word: "ಥಳಥಳ", + image: getAssetUrl(s3Assets.ಥಳಥಳImg), + audio: getAssetAudioUrl(s3Assets.ಥಳಥಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಥಳಥಳAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಥಳಥಳAudio), + }, + { + id: 83, + title: "Letter", + letter: "ಥ", + word: "ರಥ", + image: getAssetUrl(s3Assets.ರಥImg), + audio: getAssetAudioUrl(s3Assets.ರಥAudio), + singleAudio: getAssetAudioUrl(s3Assets.ರಥAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ರಥAudio), + }, + ], + }, + { + letter: "ಢ", + items: [ + { + id: 84, + title: "Letter", + letter: "ಢ", + word: "ಢಣಢಣ", + image: getAssetUrl(s3Assets.ಢಣಢಣImg), + audio: getAssetAudioUrl(s3Assets.ಢಣಢಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಢಣಢಣAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಢಣಢಣAudio), + }, + { + id: 85, + title: "Letter", + letter: "ಢ", + word: "ಗೂಢ", + image: getAssetUrl(s3Assets.ಗಢImg), + audio: getAssetAudioUrl(s3Assets.ಗಢAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಢAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಗಢAudio), + }, + ], + }, + { + letter: "ಭ", + items: [ + { + id: 86, + title: "Letter", + letter: "ಭ", + word: "ಭವನ", + image: getAssetUrl(s3Assets.ಭವನImg), + audio: getAssetAudioUrl(s3Assets.ಭವನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಭವನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಭವನAudio), + }, + { + id: 87, + title: "Letter", + letter: "ಭ", + word: "ಆಭರಣ", + image: getAssetUrl(s3Assets.ಆಭರಣImg), + audio: getAssetAudioUrl(s3Assets.ಆಭರಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಭರಣAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಆಭರಣAudio), + }, + { + id: 88, + title: "Letter", + letter: "ಭ", + word: "ವೃಷಭ", + image: getAssetUrl(s3Assets.ವಷಭImg), + audio: getAssetAudioUrl(s3Assets.ವಷಭAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವಷಭAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ವಷಭAudio), + }, + ], + }, + { + letter: "ಘ", + items: [ + { + id: 89, + title: "Letter", + letter: "ಘ", + word: "ಘಟ", + image: getAssetUrl(s3Assets.ಘಟImg), + audio: getAssetAudioUrl(s3Assets.ಘಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಘಟAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಘಟAudio), + }, + ], + }, + { + letter: "ಠ", + items: [ + { + id: 91, + title: "Letter", + letter: "ಠ", + word: "ಠಕ್ಕ", + image: getAssetUrl(s3Assets.ಠಕಕImg), + audio: getAssetAudioUrl(s3Assets.ಠಕಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಠಕಕAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಠಕಕAudio), + }, + { + id: 92, + title: "Letter", + letter: "ಠ", + word: "ಜಠರ", + image: getAssetUrl(s3Assets.ಜಠರImg), + audio: getAssetAudioUrl(s3Assets.ಜಠರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜಠರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಜಠರAudio), + }, + { + id: 93, + title: "Letter", + letter: "ಠ", + word: "ಕಂಠ", + image: getAssetUrl(s3Assets.ಕಠImg), + audio: getAssetAudioUrl(s3Assets.ಕಠAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಠAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಠAudio), + }, + ], + }, + { + letter: "ಫ", + items: [ + { + id: 94, + title: "Letter", + letter: "ಫ", + word: "ಫಲ", + image: getAssetUrl(s3Assets.ಫಲImg), + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಫಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಫಲAudio), + }, + { + id: 95, + title: "Letter", + letter: "ಫ", + word: "ಸಫಲ", + image: getAssetUrl(s3Assets.ಸಫಲImg), + audio: getAssetAudioUrl(s3Assets.ಸಫಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸಫಲAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಸಫಲAudio), + }, + { + id: 96, + title: "Letter", + letter: "ಫ", + word: "ಕಫ", + image: getAssetUrl(s3Assets.ಕಫImg), + audio: getAssetAudioUrl(s3Assets.ಕಫAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಫAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಫAudio), + }, + ], + }, + { + letter: "ಝ", + items: [ + { + id: 97, + title: "Letter", + letter: "ಝ", + word: "ಝರಿ", + image: getAssetUrl(s3Assets.ಝರImg), + audio: getAssetAudioUrl(s3Assets.ಝರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಝರAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಝರAudio), + }, + ], + }, + { + letter: "ಖ", + items: [ + { + id: 99, + title: "Letter", + letter: "ಖ", + word: "ಖಗ", + image: getAssetUrl(s3Assets.ಖಗImg), + audio: getAssetAudioUrl(s3Assets.ಖಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಖಗAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಖಗAudio), + }, + { + id: 100, + title: "Letter", + letter: "ಖ", + word: "ಲೇಖನ", + image: getAssetUrl(s3Assets.ಲಖನImg), + audio: getAssetAudioUrl(s3Assets.ಲಖನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಲಖನAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಲಖನAudio), + }, + { + id: 101, + title: "Letter", + letter: "ಖ", + word: "ಪಂಖ", + image: getAssetUrl(s3Assets.ಪಖImg), + audio: getAssetAudioUrl(s3Assets.ಪಖAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪಖAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಪಖAudio), + }, + ], + }, + { + letter: "ಅಂ", + items: [ + { + id: 102, + title: "Letter", + letter: "ಅಂ", + word: "ಅಂಗಳ", + image: getAssetUrl(s3Assets.ಅಗಳImg), + audio: getAssetAudioUrl(s3Assets.ಅಗಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಅಗಳAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಅಗಳAudio), + }, + ], + }, + { + letter: "ಅಃ", + items: [ + { + id: 103, + title: "Letter", + letter: "ಅಃ", + word: "ಅಃ", + image: getAssetUrl(s3Assets.ಅImg), + audio: getAssetAudioUrl(s3Assets.ಅAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಅAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಅAudio), + }, + ], + }, + { + letter: "ಙ", + items: [ + { + id: 104, + title: "Letter", + letter: "ಙ", + word: "ಙ", + image: getAssetUrl(s3Assets.ಙImg), + audio: getAssetAudioUrl(s3Assets.ಙAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಙAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಙAudio), + }, + ], + }, + { + letter: "ಞ", + items: [ + { + id: 105, + title: "Letter", + letter: "ಞ", + word: "ಞ", + image: getAssetUrl(s3Assets.ಞImg), + audio: getAssetAudioUrl(s3Assets.ಞAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಞAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಞAudio), + }, + ], + }, + { + letter: "ಕ್ಷ", + items: [ + { + id: 106, + title: "Letter", + letter: "ಕ್ಷ", + word: "ಕ್ಷ", + image: getAssetUrl(s3Assets.ಕಷImg), + audio: getAssetAudioUrl(s3Assets.ಕಷAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಷAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಕಷAudio), + }, + ], + }, + { + letter: "ಜ್ಞ", + items: [ + { + id: 107, + title: "Letter", + letter: "ಜ್ಞ", + word: "ಜ್ಞ", + image: getAssetUrl(s3Assets.ಜಞImg), + audio: getAssetAudioUrl(s3Assets.ಜಞAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜಞAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ಜಞAudio), + }, + ], + }, +]; + +export const dataHi = [ + { + letter: "अ", + items: [ + { + id: 1, + title: "Letter", + letter: "अ", + word: "अनार", + image: getAssetUrl(s3Assets.अनरImg), + audio: getAssetAudioUrl(s3Assets.अनरAudio), + singleAudio: getAssetAudioUrl(s3Assets.अनरAudio), + }, + ], + }, + { + letter: "आ", + items: [ + { + id: 2, + title: "Letter", + letter: "आ", + word: "आम", + image: getAssetUrl(s3Assets.आमImg), + audio: getAssetAudioUrl(s3Assets.आमAudio), + singleAudio: getAssetAudioUrl(s3Assets.आमAudio), + }, + { + id: 3, + title: "Letter", + letter: "आ", + word: "कछुआ", + image: getAssetUrl(s3Assets.कछआImg), + audio: getAssetAudioUrl(s3Assets.कछआAudio), + singleAudio: getAssetAudioUrl(s3Assets.कछआAudio), + }, + ], + }, + { + letter: "इ", + items: [ + { + id: 4, + title: "Letter", + letter: "इ", + word: "इमली", + image: getAssetUrl(s3Assets.इमलImg), + audio: getAssetAudioUrl(s3Assets.इमलAudio), + singleAudio: getAssetAudioUrl(s3Assets.इमलAudio), + }, + { + id: 5, + title: "Letter", + letter: "इ", + word: "साइकिल", + image: getAssetUrl(s3Assets.सइकलImg), + audio: getAssetAudioUrl(s3Assets.सइकलAudio), + singleAudio: getAssetAudioUrl(s3Assets.सइकलAudio), + }, + ], + }, + { + letter: "ई", + items: [ + { + id: 6, + title: "Letter", + letter: "ई", + word: "ईख", + image: getAssetUrl(s3Assets.ईखImg), + audio: getAssetAudioUrl(s3Assets.ईखAudio), + singleAudio: getAssetAudioUrl(s3Assets.ईखAudio), + }, + { + id: 7, + title: "Letter", + letter: "ई", + word: "नई", + image: getAssetUrl(s3Assets.नईImg), + audio: getAssetAudioUrl(s3Assets.नईAudio), + singleAudio: getAssetAudioUrl(s3Assets.नईAudio), + }, + ], + }, + { + letter: "उ", + items: [ + { + id: 8, + title: "Letter", + letter: "उ", + word: "उड़", + image: getAssetUrl(s3Assets.उडImg), + audio: getAssetAudioUrl(s3Assets.उडAudio), + singleAudio: getAssetAudioUrl(s3Assets.उडAudio), + }, + ], + }, + { + letter: "ऊ", + items: [ + { + id: 9, + title: "Letter", + letter: "ऊ", + word: "ऊन", + image: getAssetUrl(s3Assets.ऊनImg), + audio: getAssetAudioUrl(s3Assets.ऊनAudio), + singleAudio: getAssetAudioUrl(s3Assets.ऊनAudio), + }, + ], + }, + { + letter: "ऋ", + items: [ + { + id: 10, + title: "Letter", + letter: "ऋ", + word: "ऋषि", + image: getAssetUrl(s3Assets.ऋषImg), + audio: getAssetAudioUrl(s3Assets.ऋषAudio), + singleAudio: getAssetAudioUrl(s3Assets.ऋषAudio), + }, + ], + }, + { + letter: "ए", + items: [ + { + id: 11, + title: "Letter", + letter: "ए", + word: "एक", + image: getAssetUrl(s3Assets.एकImg), + audio: getAssetAudioUrl(s3Assets.एकAudio), + singleAudio: getAssetAudioUrl(s3Assets.एकAudio), + }, + { + id: 12, + title: "Letter", + letter: "ए", + word: "पढ़िए", + image: getAssetUrl(s3Assets.पढएImg), + audio: getAssetAudioUrl(s3Assets.पढएAudio), + singleAudio: getAssetAudioUrl(s3Assets.पढएAudio), + }, + ], + }, + { + letter: "ऐ", + items: [ + { + id: 13, + title: "Letter", + letter: "ऐ", + word: "ऐनक", + image: getAssetUrl(s3Assets.ऐनकImg), + audio: getAssetAudioUrl(s3Assets.ऐनकAudio), + singleAudio: getAssetAudioUrl(s3Assets.ऐनकAudio), + }, + ], + }, + { + letter: "ओ", + items: [ + { + id: 14, + title: "Letter", + letter: "ओ", + word: "ओखल", + image: getAssetUrl(s3Assets.ओखलImg), + audio: getAssetAudioUrl(s3Assets.ओखलAudio), + singleAudio: getAssetAudioUrl(s3Assets.ओखलAudio), + }, + ], + }, + { + letter: "औ", + items: [ + { + id: 15, + title: "Letter", + letter: "औ", + word: "औरत", + image: getAssetUrl(s3Assets.औरतImg), + audio: getAssetAudioUrl(s3Assets.औरतAudio), + singleAudio: getAssetAudioUrl(s3Assets.औरतAudio), + }, + ], + }, + { + letter: "अं", + items: [ + { + id: 16, + title: "Letter", + letter: "अं", + word: "अंगूर", + image: getAssetUrl(s3Assets.अगरImg), + audio: getAssetAudioUrl(s3Assets.अगरAudio), + singleAudio: getAssetAudioUrl(s3Assets.अगरAudio), + }, + ], + }, + { + letter: "क", + items: [ + { + id: 17, + title: "Letter", + letter: "क", + word: "कबूतर", + image: getAssetUrl(s3Assets.कबतरImg), + audio: getAssetAudioUrl(s3Assets.कबतरAudio), + singleAudio: getAssetAudioUrl(s3Assets.कबतरAudio), + }, + { + id: 18, + title: "Letter", + letter: "क", + word: "बकरी", + image: getAssetUrl(s3Assets.बकरImg), + audio: getAssetAudioUrl(s3Assets.बकरAudio), + singleAudio: getAssetAudioUrl(s3Assets.बकरAudio), + }, + { + id: 19, + title: "Letter", + letter: "क", + word: "नमक", + image: getAssetUrl(s3Assets.नमकImg), + audio: getAssetAudioUrl(s3Assets.नमकAudio), + singleAudio: getAssetAudioUrl(s3Assets.नमकAudio), + }, + ], + }, + { + letter: "ख", + items: [ + { + id: 20, + title: "Letter", + letter: "ख", + word: "खरगोश", + image: getAssetUrl(s3Assets.खरगशImg), + audio: getAssetAudioUrl(s3Assets.खरगशAudio), + singleAudio: getAssetAudioUrl(s3Assets.खरगशAudio), + }, + { + id: 21, + title: "Letter", + letter: "ख", + word: "लेखन", + image: getAssetUrl(s3Assets.लखनImg), + audio: getAssetAudioUrl(s3Assets.लखनAudio), + singleAudio: getAssetAudioUrl(s3Assets.लखनAudio), + }, + { + id: 22, + title: "Letter", + letter: "ख", + word: "भूख", + image: getAssetUrl(s3Assets.भखImg), + audio: getAssetAudioUrl(s3Assets.भखAudio), + singleAudio: getAssetAudioUrl(s3Assets.भखAudio), + }, + ], + }, + { + letter: "ग", + items: [ + { + id: 23, + title: "Letter", + letter: "ग", + word: "गधा", + image: getAssetUrl(s3Assets.गधImg), + audio: getAssetAudioUrl(s3Assets.गधAudio), + singleAudio: getAssetAudioUrl(s3Assets.गधAudio), + }, + { + id: 24, + title: "Letter", + letter: "ग", + word: "नगर", + image: getAssetUrl(s3Assets.नगरImg), + audio: getAssetAudioUrl(s3Assets.नगरAudio), + singleAudio: getAssetAudioUrl(s3Assets.नगरAudio), + }, + { + id: 25, + title: "Letter", + letter: "ग", + word: "लोग", + image: getAssetUrl(s3Assets.लगImg), + audio: getAssetAudioUrl(s3Assets.लगAudio), + singleAudio: getAssetAudioUrl(s3Assets.लगAudio), + }, + ], + }, + { + letter: "घ", + items: [ + { + id: 26, + title: "Letter", + letter: "घ", + word: "घर", + image: getAssetUrl(s3Assets.घरImg), + audio: getAssetAudioUrl(s3Assets.घरAudio), + singleAudio: getAssetAudioUrl(s3Assets.घरAudio), + }, + { + id: 27, + title: "Letter", + letter: "घ", + word: "घुँघरू", + image: getAssetUrl(s3Assets.घघरImg), + audio: getAssetAudioUrl(s3Assets.घघरAudio), + singleAudio: getAssetAudioUrl(s3Assets.घघरAudio), + }, + { + id: 28, + title: "Letter", + letter: "घ", + word: "बाघ", + image: getAssetUrl(s3Assets.बघImg), + audio: getAssetAudioUrl(s3Assets.बघAudio), + singleAudio: getAssetAudioUrl(s3Assets.बघAudio), + }, + ], + }, + { + letter: "च", + items: [ + { + id: 29, + title: "Letter", + letter: "च", + word: "चढ़", + image: getAssetUrl(s3Assets.चढImg), + audio: getAssetAudioUrl(s3Assets.चढAudio), + singleAudio: getAssetAudioUrl(s3Assets.चढAudio), + }, + { + id: 30, + title: "Letter", + letter: "च", + word: "खिचड़ी", + image: getAssetUrl(s3Assets.खचडImg), + audio: getAssetAudioUrl(s3Assets.खचडAudio), + singleAudio: getAssetAudioUrl(s3Assets.खचडAudio), + }, + { + id: 31, + title: "Letter", + letter: "च", + word: "पाँच", + image: getAssetUrl(s3Assets.पचImg), + audio: getAssetAudioUrl(s3Assets.पचAudio), + singleAudio: getAssetAudioUrl(s3Assets.पचAudio), + }, + ], + }, + { + letter: "छ", + items: [ + { + id: 32, + title: "Letter", + letter: "छ", + word: "छत", + image: getAssetUrl(s3Assets.छतImg), + audio: getAssetAudioUrl(s3Assets.छतAudio), + singleAudio: getAssetAudioUrl(s3Assets.छतAudio), + }, + { + id: 33, + title: "Letter", + letter: "छ", + word: "मछली", + image: getAssetUrl(s3Assets.मछलImg), + audio: getAssetAudioUrl(s3Assets.मछलAudio), + singleAudio: getAssetAudioUrl(s3Assets.मछलAudio), + }, + { + id: 34, + title: "Letter", + letter: "छ", + word: "पूछ", + image: getAssetUrl(s3Assets.पछImg), + audio: getAssetAudioUrl(s3Assets.पछAudio), + singleAudio: getAssetAudioUrl(s3Assets.पछAudio), + }, + ], + }, + { + letter: "ज", + items: [ + { + id: 35, + title: "Letter", + letter: "ज", + word: "जग", + image: getAssetUrl(s3Assets.जगImg), + audio: getAssetAudioUrl(s3Assets.जगAudio), + singleAudio: getAssetAudioUrl(s3Assets.जगAudio), + }, + { + id: 36, + title: "Letter", + letter: "ज", + word: "गाजर", + image: getAssetUrl(s3Assets.गजरImg), + audio: getAssetAudioUrl(s3Assets.गजरAudio), + singleAudio: getAssetAudioUrl(s3Assets.गजरAudio), + }, + { + id: 37, + title: "Letter", + letter: "ज", + word: "सूरज", + image: getAssetUrl(s3Assets.सरजImg), + audio: getAssetAudioUrl(s3Assets.सरजAudio), + singleAudio: getAssetAudioUrl(s3Assets.सरजAudio), + }, + ], + }, + { + letter: "झ", + items: [ + { + id: 38, + title: "Letter", + letter: "झ", + word: "झरना", + image: getAssetUrl(s3Assets.झरनImg), + audio: getAssetAudioUrl(s3Assets.झरनAudio), + singleAudio: getAssetAudioUrl(s3Assets.झरनAudio), + }, + ], + }, + { + letter: "ट", + items: [ + { + id: 39, + title: "Letter", + letter: "ट", + word: "टमाटर", + image: getAssetUrl(s3Assets.टमटरImg), + audio: getAssetAudioUrl(s3Assets.टमटरAudio), + singleAudio: getAssetAudioUrl(s3Assets.टमटरAudio), + }, + { + id: 40, + title: "Letter", + letter: "ट", + word: "मटर", + image: getAssetUrl(s3Assets.मटरImg), + audio: getAssetAudioUrl(s3Assets.मटरAudio), + singleAudio: getAssetAudioUrl(s3Assets.मटरAudio), + }, + { + id: 41, + title: "Letter", + letter: "ट", + word: "ऊँट", + image: getAssetUrl(s3Assets.ऊटImg), + audio: getAssetAudioUrl(s3Assets.ऊटAudio), + singleAudio: getAssetAudioUrl(s3Assets.ऊटAudio), + }, + ], + }, + { + letter: "ठ", + items: [ + { + id: 42, + title: "Letter", + letter: "ठ", + word: "ठठेरा", + image: getAssetUrl(s3Assets.ठठरImg), + audio: getAssetAudioUrl(s3Assets.ठठरAudio), + singleAudio: getAssetAudioUrl(s3Assets.ठठरAudio), + }, + { + id: 43, + title: "Letter", + letter: "ठ", + word: "गुठली", + image: getAssetUrl(s3Assets.गठलImg), + audio: getAssetAudioUrl(s3Assets.गठलAudio), + singleAudio: getAssetAudioUrl(s3Assets.गठलAudio), + }, + { + id: 44, + title: "Letter", + letter: "ठ", + word: "आठ", + image: getAssetUrl(s3Assets.आठImg), + audio: getAssetAudioUrl(s3Assets.आठAudio), + singleAudio: getAssetAudioUrl(s3Assets.आठAudio), + }, + ], + }, + { + letter: "ड", + items: [ + { + id: 45, + title: "Letter", + letter: "ड", + word: "डमरू", + image: getAssetUrl(s3Assets.डमरImg), + audio: getAssetAudioUrl(s3Assets.डमरAudio), + singleAudio: getAssetAudioUrl(s3Assets.डमरAudio), + }, + { + id: 46, + title: "Letter", + letter: "ड", + word: "पेड़", + image: getAssetUrl(s3Assets.पडImg), + audio: getAssetAudioUrl(s3Assets.पडAudio), + singleAudio: getAssetAudioUrl(s3Assets.पडAudio), + }, + ], + }, + { + letter: "ढ", + items: [ + { + id: 47, + title: "Letter", + letter: "ढ", + word: "ढक्कन", + image: getAssetUrl(s3Assets.ढक्कनImg), + audio: getAssetAudioUrl(s3Assets.ढढक्कनAudio), + singleAudio: getAssetAudioUrl(s3Assets.ढढक्कनAudio), + }, + { + id: 48, + title: "Letter", + letter: "ढ", + word: "मेंढक", + image: getAssetUrl(s3Assets.मढकImg), + audio: getAssetAudioUrl(s3Assets.मढकAudio), + singleAudio: getAssetAudioUrl(s3Assets.मढकAudio), + }, + ], + }, + { + letter: "ण", + items: [ + { + id: 49, + title: "Letter", + letter: "ण", + word: "लवण", + image: getAssetUrl(s3Assets.लवणImg), + audio: getAssetAudioUrl(s3Assets.लवणAudio), + singleAudio: getAssetAudioUrl(s3Assets.लवणAudio), + }, + ], + }, + { + letter: "त", + items: [ + { + id: 50, + title: "Letter", + letter: "त", + word: "तट", + image: getAssetUrl(s3Assets.तटImg), + audio: getAssetAudioUrl(s3Assets.तटAudio), + singleAudio: getAssetAudioUrl(s3Assets.तटAudio), + }, + { + id: 51, + title: "Letter", + letter: "त", + word: "सुतली", + image: getAssetUrl(s3Assets.सतलImg), + audio: getAssetAudioUrl(s3Assets.सतलAudio), + singleAudio: getAssetAudioUrl(s3Assets.सतलAudio), + }, + { + id: 52, + title: "Letter", + letter: "त", + word: "रात", + image: getAssetUrl(s3Assets.रतImg), + audio: getAssetAudioUrl(s3Assets.रतAudio), + singleAudio: getAssetAudioUrl(s3Assets.रतAudio), + }, + ], + }, + { + letter: "थ", + items: [ + { + id: 53, + title: "Letter", + letter: "थ", + word: "थक", + image: getAssetUrl(s3Assets.थकImg), + audio: getAssetAudioUrl(s3Assets.थकAudio), + singleAudio: getAssetAudioUrl(s3Assets.थकAudio), + }, + { + id: 54, + title: "Letter", + letter: "थ", + word: "हाथ", + image: getAssetUrl(s3Assets.हथImg), + audio: getAssetAudioUrl(s3Assets.हथAudio), + singleAudio: getAssetAudioUrl(s3Assets.हथAudio), + }, + ], + }, + { + letter: "द", + items: [ + { + id: 55, + title: "Letter", + letter: "द", + word: "दरवाजा", + image: getAssetUrl(s3Assets.दरवजImg), + audio: getAssetAudioUrl(s3Assets.दरवजAudio), + singleAudio: getAssetAudioUrl(s3Assets.दरवजAudio), + }, + { + id: 56, + title: "Letter", + letter: "द", + word: "बादल", + image: getAssetUrl(s3Assets.बदलImg), + audio: getAssetAudioUrl(s3Assets.बदलAudio), + singleAudio: getAssetAudioUrl(s3Assets.बदलAudio), + }, + { + id: 57, + title: "Letter", + letter: "द", + word: "आनंद", + image: getAssetUrl(s3Assets.आनदImg), + audio: getAssetAudioUrl(s3Assets.आनदAudio), + singleAudio: getAssetAudioUrl(s3Assets.आनदAudio), + }, + ], + }, + { + letter: "ध", + items: [ + { + id: 58, + title: "Letter", + letter: "ध", + word: "धनुष", + image: getAssetUrl(s3Assets.धनषImg), + audio: getAssetAudioUrl(s3Assets.धनषAudio), + singleAudio: getAssetAudioUrl(s3Assets.धनषAudio), + }, + { + id: 59, + title: "Letter", + letter: "ध", + word: "इधर", + image: getAssetUrl(s3Assets.इधरImg), + audio: getAssetAudioUrl(s3Assets.इधरAudio), + singleAudio: getAssetAudioUrl(s3Assets.इधरAudio), + }, + { + id: 60, + title: "Letter", + letter: "ध", + word: "दूध", + image: getAssetUrl(s3Assets.दधImg), + audio: getAssetAudioUrl(s3Assets.दधAudio), + singleAudio: getAssetAudioUrl(s3Assets.दधAudio), + }, + ], + }, + { + letter: "न", + items: [ + { + id: 61, + title: "Letter", + letter: "न", + word: "नल", + image: getAssetUrl(s3Assets.नलImg), + audio: getAssetAudioUrl(s3Assets.नलAudio), + singleAudio: getAssetAudioUrl(s3Assets.नलAudio), + }, + { + id: 62, + title: "Letter", + letter: "न", + word: "जानवर", + image: getAssetUrl(s3Assets.जनवरImg), + audio: getAssetAudioUrl(s3Assets.जनवरAudio), + singleAudio: getAssetAudioUrl(s3Assets.जनवरAudio), + }, + { + id: 63, + title: "Letter", + letter: "न", + word: "बहन", + image: getAssetUrl(s3Assets.बहनImg), + audio: getAssetAudioUrl(s3Assets.बहनAudio), + singleAudio: getAssetAudioUrl(s3Assets.बहनAudio), + }, + ], + }, + { + letter: "प", + items: [ + { + id: 64, + title: "Letter", + letter: "प", + word: "पतंग", + image: getAssetUrl(s3Assets.पतगImg), + audio: getAssetAudioUrl(s3Assets.पतगAudio), + singleAudio: getAssetAudioUrl(s3Assets.पतगAudio), + }, + { + id: 65, + title: "Letter", + letter: "प", + word: "कपड़े", + image: getAssetUrl(s3Assets.कपडImg), + audio: getAssetAudioUrl(s3Assets.कपडAudio), + singleAudio: getAssetAudioUrl(s3Assets.कपडAudio), + }, + { + id: 66, + title: "Letter", + letter: "प", + word: "साँप", + image: getAssetUrl(s3Assets.सपImg), + audio: getAssetAudioUrl(s3Assets.सपAudio), + singleAudio: getAssetAudioUrl(s3Assets.सपAudio), + }, + ], + }, + { + letter: "फ", + items: [ + { + id: 67, + title: "Letter", + letter: "फ", + word: "फल", + image: getAssetUrl(s3Assets.फलImg), + audio: getAssetAudioUrl(s3Assets.फलAudio), + singleAudio: getAssetAudioUrl(s3Assets.फलAudio), + }, + { + id: 68, + title: "Letter", + letter: "फ", + word: "सफल", + image: getAssetUrl(s3Assets.सफलImg), + audio: getAssetAudioUrl(s3Assets.सफलAudio), + singleAudio: getAssetAudioUrl(s3Assets.सफलAudio), + }, + ], + }, + { + letter: "ब", + items: [ + { + id: 69, + title: "Letter", + letter: "ब", + word: "बतख", + image: getAssetUrl(s3Assets.बतखImg), + audio: getAssetAudioUrl(s3Assets.बतखAudio), + singleAudio: getAssetAudioUrl(s3Assets.बतखAudio), + }, + { + id: 70, + title: "Letter", + letter: "ब", + word: "सुबह", + image: getAssetUrl(s3Assets.सबहImg), + audio: getAssetAudioUrl(s3Assets.सबहAudio), + singleAudio: getAssetAudioUrl(s3Assets.सबहAudio), + }, + { + id: 71, + title: "Letter", + letter: "ब", + word: "सेब", + image: getAssetUrl(s3Assets.सबImg), + audio: getAssetAudioUrl(s3Assets.सबAudio), + singleAudio: getAssetAudioUrl(s3Assets.सबAudio), + }, + ], + }, + { + letter: "भ", + items: [ + { + id: 72, + title: "Letter", + letter: "भ", + word: "भय", + image: getAssetUrl(s3Assets.भयImg), + audio: getAssetAudioUrl(s3Assets.भयAudio), + singleAudio: getAssetAudioUrl(s3Assets.भयAudio), + }, + { + id: 73, + title: "Letter", + letter: "भ", + word: "अभय", + image: getAssetUrl(s3Assets.अभयImg), + audio: getAssetAudioUrl(s3Assets.अभयAudio), + singleAudio: getAssetAudioUrl(s3Assets.अभयAudio), + }, + { + id: 74, + title: "Letter", + letter: "भ", + word: "नभ", + image: getAssetUrl(s3Assets.नभImg), + audio: getAssetAudioUrl(s3Assets.नभAudio), + singleAudio: getAssetAudioUrl(s3Assets.नभAudio), + }, + ], + }, + { + letter: "म", + items: [ + { + id: 75, + title: "Letter", + letter: "म", + word: "मछली", + image: getAssetUrl(s3Assets.मछल2Img), + audio: getAssetAudioUrl(s3Assets.मछल2Audio), + singleAudio: getAssetAudioUrl(s3Assets.मछल2Audio), + }, + { + id: 76, + title: "Letter", + letter: "म", + word: "गमला", + image: getAssetUrl(s3Assets.गमलImg), + audio: getAssetAudioUrl(s3Assets.गमलAudio), + singleAudio: getAssetAudioUrl(s3Assets.गमलAudio), + }, + { + id: 77, + title: "Letter", + letter: "म", + word: "कदम", + image: getAssetUrl(s3Assets.कदमImg), + audio: getAssetAudioUrl(s3Assets.कदमAudio), + singleAudio: getAssetAudioUrl(s3Assets.कदमAudio), + }, + ], + }, + { + letter: "य", + items: [ + { + id: 78, + title: "Letter", + letter: "य", + word: "यह", + image: getAssetUrl(s3Assets.यहImg), + audio: getAssetAudioUrl(s3Assets.यहAudio), + singleAudio: getAssetAudioUrl(s3Assets.यहAudio), + }, + { + id: 79, + title: "Letter", + letter: "य", + word: "पायल", + image: getAssetUrl(s3Assets.पयलImg), + audio: getAssetAudioUrl(s3Assets.पयलAudio), + singleAudio: getAssetAudioUrl(s3Assets.पयलAudio), + }, + { + id: 80, + title: "Letter", + letter: "य", + word: "गाय", + image: getAssetUrl(s3Assets.गयImg), + audio: getAssetAudioUrl(s3Assets.गयAudio), + singleAudio: getAssetAudioUrl(s3Assets.गयAudio), + }, + ], + }, + { + letter: "र", + items: [ + { + id: 81, + title: "Letter", + letter: "र", + word: "रथ", + image: getAssetUrl(s3Assets.रथImg), + audio: getAssetAudioUrl(s3Assets.रथAudio), + singleAudio: getAssetAudioUrl(s3Assets.रथAudio), + }, + { + id: 82, + title: "Letter", + letter: "र", + word: "भारत", + image: getAssetUrl(s3Assets.भरतImg), + audio: getAssetAudioUrl(s3Assets.भरतAudio), + singleAudio: getAssetAudioUrl(s3Assets.भरतAudio), + }, + { + id: 83, + title: "Letter", + letter: "र", + word: "चार", + image: getAssetUrl(s3Assets.चरImg), + audio: getAssetAudioUrl(s3Assets.चरAudio), + singleAudio: getAssetAudioUrl(s3Assets.चरAudio), + }, + ], + }, + { + letter: "ल", + items: [ + { + id: 84, + title: "Letter", + letter: "ल", + word: "लड़का", + image: getAssetUrl(s3Assets.लडकImg), + audio: getAssetAudioUrl(s3Assets.लडकAudio), + singleAudio: getAssetAudioUrl(s3Assets.लडकAudio), + }, + { + id: 85, + title: "Letter", + letter: "ल", + word: "चलना", + image: getAssetUrl(s3Assets.चलनImg), + audio: getAssetAudioUrl(s3Assets.चलनAudio), + singleAudio: getAssetAudioUrl(s3Assets.चलनAudio), + }, + { + id: 86, + title: "Letter", + letter: "ल", + word: "बाल", + image: getAssetUrl(s3Assets.बलImg), + audio: getAssetAudioUrl(s3Assets.बलAudio), + singleAudio: getAssetAudioUrl(s3Assets.बलAudio), + }, + ], + }, + { + letter: "व", + items: [ + { + id: 87, + title: "Letter", + letter: "व", + word: "वन", + image: getAssetUrl(s3Assets.वनImg), + audio: getAssetAudioUrl(s3Assets.वनAudio), + singleAudio: getAssetAudioUrl(s3Assets.वनAudio), + }, + { + id: 88, + title: "Letter", + letter: "व", + word: "चावल", + image: getAssetUrl(s3Assets.चवलImg), + audio: getAssetAudioUrl(s3Assets.चवलAudio), + singleAudio: getAssetAudioUrl(s3Assets.चवलAudio), + }, + { + id: 89, + title: "Letter", + letter: "व", + word: "नाव", + image: getAssetUrl(s3Assets.नवImg), + audio: getAssetAudioUrl(s3Assets.नवAudio), + singleAudio: getAssetAudioUrl(s3Assets.नवAudio), + }, + ], + }, + { + letter: "श", + items: [ + { + id: 90, + title: "Letter", + letter: "श", + word: "शहर", + image: getAssetUrl(s3Assets.शहरImg), + audio: getAssetAudioUrl(s3Assets.शहरAudio), + singleAudio: getAssetAudioUrl(s3Assets.शहरAudio), + }, + { + id: 91, + title: "Letter", + letter: "श", + word: "बारिश", + image: getAssetUrl(s3Assets.बरशImg), + audio: getAssetAudioUrl(s3Assets.बरशAudio), + singleAudio: getAssetAudioUrl(s3Assets.बरशAudio), + }, + ], + }, + { + letter: "ष", + items: [ + { + id: 92, + title: "Letter", + letter: "ष", + word: "षट्कोण", + image: getAssetUrl(s3Assets.षटकणImg), + audio: getAssetAudioUrl(s3Assets.षटकणAudio), + singleAudio: getAssetAudioUrl(s3Assets.षटकणAudio), + }, + { + id: 93, + title: "Letter", + letter: "ष", + word: "विषय", + image: getAssetUrl(s3Assets.वषयImg), + audio: getAssetAudioUrl(s3Assets.वषयAudio), + singleAudio: getAssetAudioUrl(s3Assets.वषयAudio), + }, + { + id: 94, + title: "Letter", + letter: "ष", + word: "धनुष", + image: getAssetUrl(s3Assets.धनष2Img), + audio: getAssetAudioUrl(s3Assets.धनष2Audio), + singleAudio: getAssetAudioUrl(s3Assets.धनष2Audio), + }, + ], + }, + { + letter: "स", + items: [ + { + id: 95, + title: "Letter", + letter: "स", + word: "समय", + image: getAssetUrl(s3Assets.समयImg), + audio: getAssetAudioUrl(s3Assets.समयAudio), + singleAudio: getAssetAudioUrl(s3Assets.समयAudio), + }, + { + id: 96, + title: "Letter", + letter: "स", + word: "आसमान", + image: getAssetUrl(s3Assets.आसमनImg), + audio: getAssetAudioUrl(s3Assets.आसमनAudio), + singleAudio: getAssetAudioUrl(s3Assets.आसमनAudio), + }, + { + id: 97, + title: "Letter", + letter: "स", + word: "घास", + image: getAssetUrl(s3Assets.घसImg), + audio: getAssetAudioUrl(s3Assets.घसAudio), + singleAudio: getAssetAudioUrl(s3Assets.घसAudio), + }, + ], + }, + { + letter: "ह", + items: [ + { + id: 98, + title: "Letter", + letter: "ह", + word: "हवा", + image: getAssetUrl(s3Assets.हवImg), + audio: getAssetAudioUrl(s3Assets.हवAudio), + singleAudio: getAssetAudioUrl(s3Assets.हवAudio), + }, + { + id: 99, + title: "Letter", + letter: "ह", + word: "महल", + image: getAssetUrl(s3Assets.महलImg), + audio: getAssetAudioUrl(s3Assets.महलAudio), + singleAudio: getAssetAudioUrl(s3Assets.महलAudio), + }, + { + id: 100, + title: "Letter", + letter: "ह", + word: "सुबह", + image: getAssetUrl(s3Assets.सबह2Img), + audio: getAssetAudioUrl(s3Assets.सबह2Audio), + singleAudio: getAssetAudioUrl(s3Assets.सबह2Audio), + }, + ], + }, + { + letter: "क्ष", + items: [ + { + id: 101, + title: "Letter", + letter: "क्ष", + word: "क्षत्रिय", + image: getAssetUrl(s3Assets.कषतरयImg), + audio: getAssetAudioUrl(s3Assets.कषतरयAudio), + singleAudio: getAssetAudioUrl(s3Assets.कषतरयAudio), + }, + { + id: 102, + title: "Letter", + letter: "क्ष", + word: "अक्षर", + image: getAssetUrl(s3Assets.अकषरImg), + audio: getAssetAudioUrl(s3Assets.अकषरAudio), + singleAudio: getAssetAudioUrl(s3Assets.अकषरAudio), + }, + ], + }, + { + letter: "त्र", + items: [ + { + id: 103, + title: "Letter", + letter: "त्र", + word: "त्रिशूल", + image: getAssetUrl(s3Assets.तरशलImg), + audio: getAssetAudioUrl(s3Assets.तरशलAudio), + singleAudio: getAssetAudioUrl(s3Assets.तरशलAudio), + }, + { + id: 104, + title: "Letter", + letter: "त्र", + word: "चित्र", + image: getAssetUrl(s3Assets.चतरImg), + audio: getAssetAudioUrl(s3Assets.चतरAudio), + singleAudio: getAssetAudioUrl(s3Assets.चतरAudio), + }, + ], + }, + { + letter: "ज्ञ", + items: [ + { + id: 105, + title: "Letter", + letter: "ज्ञ", + word: "ज्ञानी", + image: getAssetUrl(s3Assets.जञनImg), + audio: getAssetAudioUrl(s3Assets.जञनAudio), + singleAudio: getAssetAudioUrl(s3Assets.जञनAudio), + }, + ], + }, +]; + +export const dataTe = [ + { + letter: "త", + items: [ + { + id: 1, + title: "Letter", + letter: "త", + word: "తబల", + image: getAssetUrl(s3Assets.తబలImg), + audio: getAssetAudioUrl(s3Assets.తతబలAudio), + singleAudio: getAssetAudioUrl(s3Assets.తతబలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.తతబలAudio), + }, + { + id: 2, + title: "Letter", + letter: "త", + word: "జాతర", + image: getAssetUrl(s3Assets.జతరImg), + audio: getAssetAudioUrl(s3Assets.జతరAudio), + singleAudio: getAssetAudioUrl(s3Assets.జతరAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.తజాతరAudio), + }, + { + id: 3, + title: "Letter", + letter: "త", + word: "ఈత", + image: getAssetUrl(s3Assets.ఈతImg), + audio: getAssetAudioUrl(s3Assets.ఈతAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఈతAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.తఈతAudio), + }, + ], + }, + { + letter: "బ", + items: [ + { + id: 4, + title: "Letter", + letter: "బ", + word: "బంతి", + image: getAssetUrl(s3Assets.బతImg), + audio: getAssetAudioUrl(s3Assets.బబంతిAudio), + singleAudio: getAssetAudioUrl(s3Assets.బబంతిAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.బబంతిAudio), + }, + { + id: 5, + title: "Letter", + letter: "బ", + word: "తబల", + image: getAssetUrl(s3Assets.తబల2Img), + audio: getAssetAudioUrl(s3Assets.తబల2Audio), + singleAudio: getAssetAudioUrl(s3Assets.తబల2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.బతబలAudio), + }, + { + id: 6, + title: "Letter", + letter: "బ", + word: "లబలబ", + image: getAssetUrl(s3Assets.లబలబImg), + audio: getAssetAudioUrl(s3Assets.లబలబAudio), + singleAudio: getAssetAudioUrl(s3Assets.లబలబAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.బలబలబAudio), + }, + ], + }, + { + letter: "ల", + items: [ + { + id: 7, + title: "Letter", + letter: "ల", + word: "లత", + image: getAssetUrl(s3Assets.లతImg), + audio: getAssetAudioUrl(s3Assets.లలతAudio), + singleAudio: getAssetAudioUrl(s3Assets.లలతAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.లలతAudio), + }, + { + id: 8, + title: "Letter", + letter: "ల", + word: "బలపం", + image: getAssetUrl(s3Assets.బలపImg), + audio: getAssetAudioUrl(s3Assets.బలపAudio), + singleAudio: getAssetAudioUrl(s3Assets.బలపAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.లబలపంAudio), + }, + { + id: 9, + title: "Letter", + letter: "ల", + word: "వెల", + image: getAssetUrl(s3Assets.వలImg), + audio: getAssetAudioUrl(s3Assets.వలAudio), + singleAudio: getAssetAudioUrl(s3Assets.వలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.లవెలAudio), + }, + ], + }, + { + letter: "క", + items: [ + { + id: 10, + title: "Letter", + letter: "క", + word: "కంజర", + image: getAssetUrl(s3Assets.కజరImg), + audio: getAssetAudioUrl(s3Assets.కకంజరAudio), + singleAudio: getAssetAudioUrl(s3Assets.కకంజరAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.కకంజరAudio), + }, + { + id: 11, + title: "Letter", + letter: "క", + word: "ఆకలి", + image: getAssetUrl(s3Assets.ఆకలImg), + audio: getAssetAudioUrl(s3Assets.ఆకలAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఆకలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.కఆకలిAudio), + }, + { + id: 12, + title: "Letter", + letter: "క", + word: "చిలుక", + image: getAssetUrl(s3Assets.చలకImg), + audio: getAssetAudioUrl(s3Assets.చలకAudio), + singleAudio: getAssetAudioUrl(s3Assets.చలకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.కచిలుకAudio), + }, + ], + }, + { + letter: "జ", + items: [ + { + id: 13, + title: "Letter", + letter: "జ", + word: "జడ", + image: getAssetUrl(s3Assets.జడImg), + audio: getAssetAudioUrl(s3Assets.జజడAudio), + singleAudio: getAssetAudioUrl(s3Assets.జజడAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.జజడAudio), + }, + { + id: 14, + title: "Letter", + letter: "జ", + word: "కంజర", + image: getAssetUrl(s3Assets.కజర2Img), + audio: getAssetAudioUrl(s3Assets.కజర2Audio), + singleAudio: getAssetAudioUrl(s3Assets.కజర2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.జకంజరAudio), + }, + { + id: 15, + title: "Letter", + letter: "జ", + word: "జలజ", + image: getAssetUrl(s3Assets.జలజImg), + audio: getAssetAudioUrl(s3Assets.జజలజAudio), + singleAudio: getAssetAudioUrl(s3Assets.జజలజAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.జజలజAudio), + }, + ], + }, + { + letter: "ర", + items: [ + { + id: 16, + title: "Letter", + letter: "ర", + word: "రవి", + image: getAssetUrl(s3Assets.రవImg), + audio: getAssetAudioUrl(s3Assets.రరవిAudio), + singleAudio: getAssetAudioUrl(s3Assets.రరవిAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.రరవిAudio), + }, + { + id: 17, + title: "Letter", + letter: "ర", + word: "గిరక", + image: getAssetUrl(s3Assets.గరకImg), + audio: getAssetAudioUrl(s3Assets.గరకAudio), + singleAudio: getAssetAudioUrl(s3Assets.గరకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.రగిరకAudio), + }, + { + id: 18, + title: "Letter", + letter: "ర", + word: "చీర", + image: getAssetUrl(s3Assets.చరImg), + audio: getAssetAudioUrl(s3Assets.చరAudio), + singleAudio: getAssetAudioUrl(s3Assets.చరAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.రచీరAudio), + }, + ], + }, + { + letter: "ఆ", + items: [ + { + id: 19, + title: "Letter", + letter: "ఆ", + word: "ఆట", + image: getAssetUrl(s3Assets.ఆటImg), + audio: getAssetAudioUrl(s3Assets.ఆఆటAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఆఆటAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఆఆటAudio), + }, + ], + }, + { + letter: "ట", + items: [ + { + id: 20, + title: "Letter", + letter: "ట", + word: "టమాట", + image: getAssetUrl(s3Assets.టమటImg), + audio: getAssetAudioUrl(s3Assets.టటమాటAudio), + singleAudio: getAssetAudioUrl(s3Assets.టటమాటAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.టటమాటAudio), + }, + { + id: 21, + title: "Letter", + letter: "ట", + word: "నాటకం", + image: getAssetUrl(s3Assets.నటకImg), + audio: getAssetAudioUrl(s3Assets.నటకAudio), + singleAudio: getAssetAudioUrl(s3Assets.నటకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.టనాటకంAudio), + }, + { + id: 22, + title: "Letter", + letter: "ట", + word: "తోట", + image: getAssetUrl(s3Assets.తటImg), + audio: getAssetAudioUrl(s3Assets.తటAudio), + singleAudio: getAssetAudioUrl(s3Assets.తటAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.టతోటAudio), + }, + ], + }, + { + letter: "ఉ", + items: [ + { + id: 23, + title: "Letter", + letter: "ఉ", + word: "ఉంగరం", + image: getAssetUrl(s3Assets.ఉగరImg), + audio: getAssetAudioUrl(s3Assets.ఉఉంగరంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉఉంగరంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఉఉంగరంAudio), + }, + ], + }, + { + letter: "గ", + items: [ + { + id: 24, + title: "Letter", + letter: "గ", + word: "గద", + image: getAssetUrl(s3Assets.గదImg), + audio: getAssetAudioUrl(s3Assets.గగదAudio), + singleAudio: getAssetAudioUrl(s3Assets.గగదAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.గగదAudio), + }, + { + id: 25, + title: "Letter", + letter: "గ", + word: "ఉంగరం", + image: getAssetUrl(s3Assets.ఉగర2Img), + audio: getAssetAudioUrl(s3Assets.ఉగర2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ఉగర2Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.గఉంగరంAudio), + }, + { + id: 26, + title: "Letter", + letter: "గ", + word: "పండుగ", + image: getAssetUrl(s3Assets.పడగImg), + audio: getAssetAudioUrl(s3Assets.పడగAudio), + singleAudio: getAssetAudioUrl(s3Assets.పడగAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.గపండుగAudio), + }, + ], + }, + { + letter: "శ", + items: [ + { + id: 27, + title: "Letter", + letter: "శ", + word: "శనగ", + image: getAssetUrl(s3Assets.శనగImg), + audio: getAssetAudioUrl(s3Assets.శశనగAudio), + singleAudio: getAssetAudioUrl(s3Assets.శశనగAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.శశనగAudio), + }, + { + id: 28, + title: "Letter", + letter: "శ", + word: "దశమి", + image: getAssetUrl(s3Assets.దశమImg), + audio: getAssetAudioUrl(s3Assets.దశమAudio), + singleAudio: getAssetAudioUrl(s3Assets.దశమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.శదశమిAudio), + }, + { + id: 29, + title: "Letter", + letter: "శ", + word: "దిశ", + image: getAssetUrl(s3Assets.దశImg), + audio: getAssetAudioUrl(s3Assets.దశAudio), + singleAudio: getAssetAudioUrl(s3Assets.దశAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.శదిశAudio), + }, + ], + }, + { + letter: "అ", + items: [ + { + id: 30, + title: "Letter", + letter: "అ", + word: "అనప", + image: getAssetUrl(s3Assets.అనపImg), + audio: getAssetAudioUrl(s3Assets.అఅనపAudio), + singleAudio: getAssetAudioUrl(s3Assets.అఅనపAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.అఅనపAudio), + }, + ], + }, + { + letter: "ప", + items: [ + { + id: 31, + title: "Letter", + letter: "ప", + word: "పంట", + image: getAssetUrl(s3Assets.పటImg), + audio: getAssetAudioUrl(s3Assets.పపంటAudio), + singleAudio: getAssetAudioUrl(s3Assets.పపంటAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.పపంటAudio), + }, + { + id: 32, + title: "Letter", + letter: "ప", + word: "చేపలు", + image: getAssetUrl(s3Assets.చపలImg), + audio: getAssetAudioUrl(s3Assets.చపలAudio), + singleAudio: getAssetAudioUrl(s3Assets.చపలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.పచేపలుAudio), + }, + { + id: 33, + title: "Letter", + letter: "ప", + word: "గంప", + image: getAssetUrl(s3Assets.గంపImg), + audio: getAssetAudioUrl(s3Assets.గంపAudio), + singleAudio: getAssetAudioUrl(s3Assets.గంపAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.గంపAudio), + }, + ], + }, + { + letter: "స", + items: [ + { + id: 34, + title: "Letter", + letter: "స", + word: "సవరం", + image: getAssetUrl(s3Assets.సవరImg), + audio: getAssetAudioUrl(s3Assets.ససవరంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ససవరంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ససవరంAudio), + }, + { + id: 35, + title: "Letter", + letter: "స", + word: "దసరా", + image: getAssetUrl(s3Assets.దసరImg), + audio: getAssetAudioUrl(s3Assets.దసరAudio), + singleAudio: getAssetAudioUrl(s3Assets.దసరAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.సదసరాAudio), + }, + { + id: 36, + title: "Letter", + letter: "స", + word: "పనస", + image: getAssetUrl(s3Assets.పనసImg), + audio: getAssetAudioUrl(s3Assets.పనసAudio), + singleAudio: getAssetAudioUrl(s3Assets.పనసAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.సపనసAudio), + }, + ], + }, + { + letter: "వ", + items: [ + { + id: 37, + title: "Letter", + letter: "వ", + word: "వల", + image: getAssetUrl(s3Assets.వల2Img), + audio: getAssetAudioUrl(s3Assets.వవలAudio), + singleAudio: getAssetAudioUrl(s3Assets.వవలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.వవలAudio), + }, + { + id: 38, + title: "Letter", + letter: "వ", + word: "లవణం", + image: getAssetUrl(s3Assets.లవణImg), + audio: getAssetAudioUrl(s3Assets.లవణAudio), + singleAudio: getAssetAudioUrl(s3Assets.లవణAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.వలవణంAudio), + }, + { + id: 39, + title: "Letter", + letter: "వ", + word: "పడవ", + image: getAssetUrl(s3Assets.పడవImg), + audio: getAssetAudioUrl(s3Assets.పడవAudio), + singleAudio: getAssetAudioUrl(s3Assets.పడవAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.వపడవAudio), + }, + ], + }, + { + letter: "ఊ", + items: [ + { + id: 40, + title: "Letter", + letter: "ఊ", + word: "ఊయల", + image: getAssetUrl(s3Assets.ఊయలImg), + audio: getAssetAudioUrl(s3Assets.ఊఊయలAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఊఊయలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఊఊయలAudio), + }, + ], + }, + { + letter: "డ", + items: [ + { + id: 41, + title: "Letter", + letter: "డ", + word: "డబ్బా", + image: getAssetUrl(s3Assets.డబబImg), + audio: getAssetAudioUrl(s3Assets.డడబ్బాAudio), + singleAudio: getAssetAudioUrl(s3Assets.డడబ్బాAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.డడబ్బాAudio), + }, + { + id: 42, + title: "Letter", + letter: "డ", + word: "అడవి", + image: getAssetUrl(s3Assets.అడవImg), + audio: getAssetAudioUrl(s3Assets.అడవAudio), + singleAudio: getAssetAudioUrl(s3Assets.అడవAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.డఅడవిAudio), + }, + { + id: 43, + title: "Letter", + letter: "డ", + word: "బండ", + image: getAssetUrl(s3Assets.బడImg), + audio: getAssetAudioUrl(s3Assets.బడAudio), + singleAudio: getAssetAudioUrl(s3Assets.బడAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.డబండAudio), + }, + ], + }, + { + letter: "ద", + items: [ + { + id: 44, + title: "Letter", + letter: "ద", + word: "దండ", + image: getAssetUrl(s3Assets.దడImg), + audio: getAssetAudioUrl(s3Assets.దదండAudio), + singleAudio: getAssetAudioUrl(s3Assets.దదండAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.దదండAudio), + }, + { + id: 45, + title: "Letter", + letter: "ద", + word: "ఉదయం", + image: getAssetUrl(s3Assets.ఉదయImg), + audio: getAssetAudioUrl(s3Assets.ఉదయAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉదయAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.దఉదయంAudio), + }, + { + id: 46, + title: "Letter", + letter: "ద", + word: "కింద", + image: getAssetUrl(s3Assets.కదImg), + audio: getAssetAudioUrl(s3Assets.కదAudio), + singleAudio: getAssetAudioUrl(s3Assets.కదAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.దకిందAudio), + }, + ], + }, + { + letter: "ఈ", + items: [ + { + id: 47, + title: "Letter", + letter: "ఈ", + word: "ఈత", + image: getAssetUrl(s3Assets.ఈత2Img), + audio: getAssetAudioUrl(s3Assets.ఈఈతAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఈఈతAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఈఈతAudio), + }, + ], + }, + { + letter: "మ", + items: [ + { + id: 48, + title: "Letter", + letter: "మ", + word: "మర", + image: getAssetUrl(s3Assets.మరImg), + audio: getAssetAudioUrl(s3Assets.మమరAudio), + singleAudio: getAssetAudioUrl(s3Assets.మమరAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.మమరAudio), + }, + { + id: 49, + title: "Letter", + letter: "మ", + word: "నెమలి", + image: getAssetUrl(s3Assets.నమలImg), + audio: getAssetAudioUrl(s3Assets.నమలAudio), + singleAudio: getAssetAudioUrl(s3Assets.నమలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.మనెమలిAudio), + }, + { + id: 50, + title: "Letter", + letter: "మ", + word: "చీమ", + image: getAssetUrl(s3Assets.చమImg), + audio: getAssetAudioUrl(s3Assets.చమAudio), + singleAudio: getAssetAudioUrl(s3Assets.చమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.మచీమAudio), + }, + ], + }, + { + letter: "చ", + items: [ + { + id: 51, + title: "Letter", + letter: "చ", + word: "చరక", + image: getAssetUrl(s3Assets.చరకImg), + audio: getAssetAudioUrl(s3Assets.చచరకాAudio), + singleAudio: getAssetAudioUrl(s3Assets.చచరకాAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.చచరకాAudio), + }, + { + id: 52, + title: "Letter", + letter: "చ", + word: "రచన", + image: getAssetUrl(s3Assets.రచనImg), + audio: getAssetAudioUrl(s3Assets.రచనAudio), + singleAudio: getAssetAudioUrl(s3Assets.రచనAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.చరచనAudio), + }, + { + id: 53, + title: "Letter", + letter: "చ", + word: "కిచకిచ", + image: getAssetUrl(s3Assets.కచకచImg), + audio: getAssetAudioUrl(s3Assets.కచకచAudio), + singleAudio: getAssetAudioUrl(s3Assets.కచకచAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.చకిచకిచAudio), + }, + ], + }, + { + letter: "ఒ", + items: [ + { + id: 54, + title: "Letter", + letter: "ఒ", + word: "ఒక", + image: getAssetUrl(s3Assets.ఒకImg), + audio: getAssetAudioUrl(s3Assets.ఒఒకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఒఒకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఒఒకAudio), + }, + ], + }, + { + letter: "ఓ", + items: [ + { + id: 55, + title: "Letter", + letter: "ఓ", + word: "ఓడ", + image: getAssetUrl(s3Assets.ఓడImg), + audio: getAssetAudioUrl(s3Assets.ఓఓడAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఓఓడAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఓఓడAudio), + }, + ], + }, + { + letter: "ఔ", + items: [ + { + id: 56, + title: "Letter", + letter: "ఔ", + word: "ఔటు", + image: getAssetUrl(s3Assets.ఔటImg), + audio: getAssetAudioUrl(s3Assets.ఔఔటుAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఔఔటుAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఔఔటుAudio), + }, + ], + }, + { + letter: "య", + items: [ + { + id: 57, + title: "Letter", + letter: "య", + word: "యమ", + image: getAssetUrl(s3Assets.యమImg), + audio: getAssetAudioUrl(s3Assets.యయమAudio), + singleAudio: getAssetAudioUrl(s3Assets.యయమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.యయమAudio), + }, + { + id: 58, + title: "Letter", + letter: "య", + word: "కాయలు", + image: getAssetUrl(s3Assets.కయలImg), + audio: getAssetAudioUrl(s3Assets.కయలAudio), + singleAudio: getAssetAudioUrl(s3Assets.కయలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.యకాయలుAudio), + }, + { + id: 59, + title: "Letter", + letter: "య", + word: "వంకాయ", + image: getAssetUrl(s3Assets.వకయImg), + audio: getAssetAudioUrl(s3Assets.వకయAudio), + singleAudio: getAssetAudioUrl(s3Assets.వకయAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.యవంకాయAudio), + }, + ], + }, + { + letter: "ఇ", + items: [ + { + id: 60, + title: "Letter", + letter: "ఇ", + word: "ఇటుక", + image: getAssetUrl(s3Assets.ఇటకImg), + audio: getAssetAudioUrl(s3Assets.ఇఇటుకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఇఇటుకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఇఇటుకAudio), + }, + ], + }, + { + letter: "ఎ", + items: [ + { + id: 61, + title: "Letter", + letter: "ఎ", + word: "ఎలుక", + image: getAssetUrl(s3Assets.ఎలకImg), + audio: getAssetAudioUrl(s3Assets.ఎఎలుకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఎఎలుకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఎఎలుకAudio), + }, + ], + }, + { + letter: "ఏ", + items: [ + { + id: 62, + title: "Letter", + letter: "ఏ", + word: "ఏనుగు", + image: getAssetUrl(s3Assets.ఏనగImg), + audio: getAssetAudioUrl(s3Assets.ఏఏనుగుAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఏఏనుగుAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఏఏనుగుAudio), + }, + ], + }, + { + letter: "ఐ", + items: [ + { + id: 63, + title: "Letter", + letter: "ఐ", + word: "ఐదు", + image: getAssetUrl(s3Assets.ఐదImg), + audio: getAssetAudioUrl(s3Assets.ఐఐదుAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఐఐదుAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఐఐదుAudio), + }, + ], + }, + { + letter: "ణ", + items: [ + { + id: 64, + title: "Letter", + letter: "ణ", + word: "గణపతి", + image: getAssetUrl(s3Assets.గణపతImg), + audio: getAssetAudioUrl(s3Assets.గణపతAudio), + singleAudio: getAssetAudioUrl(s3Assets.గణపతAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ణగణపతిAudio), + }, + { + id: 65, + title: "Letter", + letter: "ణ", + word: "వీణ", + image: getAssetUrl(s3Assets.వణImg), + audio: getAssetAudioUrl(s3Assets.వీణSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.వీణSingleAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ణవీణAudio), + }, + ], + }, + { + letter: "ళ", + items: [ + { + id: 66, + title: "Letter", + letter: "ళ", + word: "తాళం", + image: getAssetUrl(s3Assets.తళImg), + audio: getAssetAudioUrl(s3Assets.తాళంSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.తాళంSingleAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ళతాళంAudio), + }, + { + id: 67, + title: "Letter", + letter: "ళ", + word: "కళ", + image: getAssetUrl(s3Assets.కళImg), + audio: getAssetAudioUrl(s3Assets.కళAudio), + singleAudio: getAssetAudioUrl(s3Assets.కళAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ళకళAudio), + }, + ], + }, + { + letter: "హ", + items: [ + { + id: 68, + title: "Letter", + letter: "హ", + word: "హంస", + image: getAssetUrl(s3Assets.హసImg), + audio: getAssetAudioUrl(s3Assets.హహంసAudio), + singleAudio: getAssetAudioUrl(s3Assets.హహంసAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.హహంసAudio), + }, + { + id: 69, + title: "Letter", + letter: "హ", + word: "వాహనం", + image: getAssetUrl(s3Assets.వహనImg), + audio: getAssetAudioUrl(s3Assets.వహనAudio), + singleAudio: getAssetAudioUrl(s3Assets.వహనAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.హవాహనంAudio), + }, + { + id: 70, + title: "Letter", + letter: "హ", + word: "గుహ", + image: getAssetUrl(s3Assets.గహImg), + audio: getAssetAudioUrl(s3Assets.గహAudio), + singleAudio: getAssetAudioUrl(s3Assets.గహAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.హగుహAudio), + }, + ], + }, + { + letter: "ఖ", + items: [ + { + id: 71, + title: "Letter", + letter: "ఖ", + word: "ఖగం", + image: getAssetUrl(s3Assets.ఖగImg), + audio: getAssetAudioUrl(s3Assets.ఖఖగంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఖఖగంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఖఖగంAudio), + }, + { + id: 72, + title: "Letter", + letter: "ఖ", + word: "ముఖము", + image: getAssetUrl(s3Assets.మఖమImg), + audio: getAssetAudioUrl(s3Assets.మఖమAudio), + singleAudio: getAssetAudioUrl(s3Assets.మఖమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఖముఖముAudio), + }, + { + id: 73, + title: "Letter", + letter: "ఖ", + word: "శంఖం", + image: getAssetUrl(s3Assets.శఖImg), + audio: getAssetAudioUrl(s3Assets.శఖAudio), + singleAudio: getAssetAudioUrl(s3Assets.శఖAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఖశంఖంAudio), + }, + ], + }, + { + letter: "ఛ", + items: [ + { + id: 74, + title: "Letter", + letter: "ఛ", + word: "ఛత్రము", + image: getAssetUrl(s3Assets.ఛతరమImg), + audio: getAssetAudioUrl(s3Assets.ఛఛత్రముAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఛఛత్రముAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఛఛత్రముAudio), + }, + { + id: 75, + title: "Letter", + letter: "ఛ", + word: "పింఛం", + image: getAssetUrl(s3Assets.పఛImg), + audio: getAssetAudioUrl(s3Assets.పఛAudio), + singleAudio: getAssetAudioUrl(s3Assets.పఛAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఛపింఛంAudio), + }, + ], + }, + { + letter: "ఠ", + items: [ + { + id: 76, + title: "Letter", + letter: "ఠ", + word: "పాఠశాల", + image: getAssetUrl(s3Assets.పఠశలImg), + audio: getAssetAudioUrl(s3Assets.పఠశలAudio), + singleAudio: getAssetAudioUrl(s3Assets.పఠశలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఠపాఠశాలAudio), + }, + { + id: 77, + title: "Letter", + letter: "ఠ", + word: "పాఠం", + image: getAssetUrl(s3Assets.పఠImg), + audio: getAssetAudioUrl(s3Assets.పాఠంSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.పాఠంSingleAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఠపాఠంAudio), + }, + ], + }, + { + letter: "ఢ", + items: [ + { + id: 78, + title: "Letter", + letter: "ఢ", + word: "ఢమఢమ", + image: getAssetUrl(s3Assets.ఢమఢమImg), + audio: getAssetAudioUrl(s3Assets.ఢఢమఢమAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఢఢమఢమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఢఢమఢమAudio), + }, + ], + }, + { + letter: "ఘ", + items: [ + { + id: 79, + title: "Letter", + letter: "ఘ", + word: "ఘటం", + image: getAssetUrl(s3Assets.ఘటImg), + audio: getAssetAudioUrl(s3Assets.ఘఘటంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఘఘటంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఘఘటంAudio), + }, + { + id: 80, + title: "Letter", + letter: "ఘ", + word: "సంఘటన", + image: getAssetUrl(s3Assets.సఘటనImg), + audio: getAssetAudioUrl(s3Assets.సఘటనAudio), + singleAudio: getAssetAudioUrl(s3Assets.సఘటనAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఘసంఘటనAudio), + }, + { + id: 81, + title: "Letter", + letter: "ఘ", + word: "మేఘం", + image: getAssetUrl(s3Assets.మఘImg), + audio: getAssetAudioUrl(s3Assets.మఘAudio), + singleAudio: getAssetAudioUrl(s3Assets.మఘAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఘమేఘంAudio), + }, + ], + }, + { + letter: "ఝ", + items: [ + { + id: 82, + title: "Letter", + letter: "ఝ", + word: "ఝషం", + image: getAssetUrl(s3Assets.ఝషImg), + audio: getAssetAudioUrl(s3Assets.ఝఝషంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఝఝషంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఝఝషంAudio), + }, + ], + }, + { + letter: "ఋ", + items: [ + { + id: 83, + title: "Letter", + letter: "ఋ", + word: "ఋషి", + image: getAssetUrl(s3Assets.ఋషImg), + audio: getAssetAudioUrl(s3Assets.ఋఋషిAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఋఋషిAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఋఋషిAudio), + }, + ], + }, + { + letter: "ష", + items: [ + { + id: 84, + title: "Letter", + letter: "ష", + word: "షరా", + image: getAssetUrl(s3Assets.షరImg), + audio: getAssetAudioUrl(s3Assets.షషరాAudio), + singleAudio: getAssetAudioUrl(s3Assets.షషరాAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.షషరాAudio), + }, + { + id: 85, + title: "Letter", + letter: "ష", + word: "విషము", + image: getAssetUrl(s3Assets.వషమImg), + audio: getAssetAudioUrl(s3Assets.వషమAudio), + singleAudio: getAssetAudioUrl(s3Assets.వషమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.షవిషముAudio), + }, + { + id: 86, + title: "Letter", + letter: "ష", + word: "ఉష", + image: getAssetUrl(s3Assets.ఉషImg), + audio: getAssetAudioUrl(s3Assets.ఉషAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉషAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.షఉషAudio), + }, + ], + }, + { + letter: "థ", + items: [ + { + id: 87, + title: "Letter", + letter: "థ", + word: "థర్మోస్", + image: getAssetUrl(s3Assets.థరమసImg), + audio: getAssetAudioUrl(s3Assets.థథర్మోస్Audio), + singleAudio: getAssetAudioUrl(s3Assets.థథర్మోస్Audio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.థథర్మోస్Audio), + }, + { + id: 88, + title: "Letter", + letter: "థ", + word: "రథము", + image: getAssetUrl(s3Assets.రథమImg), + audio: getAssetAudioUrl(s3Assets.రథమAudio), + singleAudio: getAssetAudioUrl(s3Assets.రథమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.థరథముAudio), + }, + { + id: 89, + title: "Letter", + letter: "థ", + word: "కథ", + image: getAssetUrl(s3Assets.కథImg), + audio: getAssetAudioUrl(s3Assets.కథAudio), + singleAudio: getAssetAudioUrl(s3Assets.కథAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.థకథAudio), + }, + ], + }, + { + letter: "ధ", + items: [ + { + id: 90, + title: "Letter", + letter: "ధ", + word: "ధనం", + image: getAssetUrl(s3Assets.ధనImg), + audio: getAssetAudioUrl(s3Assets.ధధనంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ధధనంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ధధనంAudio), + }, + { + id: 91, + title: "Letter", + letter: "ధ", + word: "క్రోధము", + image: getAssetUrl(s3Assets.కరధమImg), + audio: getAssetAudioUrl(s3Assets.కరధమAudio), + singleAudio: getAssetAudioUrl(s3Assets.కరధమAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ధక్రోధముAudio), + }, + { + id: 92, + title: "Letter", + letter: "ధ", + word: "బాధ", + image: getAssetUrl(s3Assets.బధImg), + audio: getAssetAudioUrl(s3Assets.బధAudio), + singleAudio: getAssetAudioUrl(s3Assets.బధAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ధబాధAudio), + }, + ], + }, + { + letter: "ఫ", + items: [ + { + id: 93, + title: "Letter", + letter: "ఫ", + word: "ఫలము", + image: getAssetUrl(s3Assets.ఫలమImg), + audio: getAssetAudioUrl(s3Assets.ఫఫలముAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఫఫలముAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఫఫలముAudio), + }, + { + id: 95, + title: "Letter", + letter: "ఫ", + word: "సీతాఫలం", + image: getAssetUrl(s3Assets.సతఫలImg), + audio: getAssetAudioUrl(s3Assets.సతఫలAudio), + singleAudio: getAssetAudioUrl(s3Assets.సతఫలAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఫసీతాఫలంAudio), + }, + ], + }, + { + letter: "భ", + items: [ + { + id: 96, + title: "Letter", + letter: "భ", + word: "భజన", + image: getAssetUrl(s3Assets.భజనImg), + audio: getAssetAudioUrl(s3Assets.భభజనAudio), + singleAudio: getAssetAudioUrl(s3Assets.భభజనAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.భభజనAudio), + }, + { + id: 97, + title: "Letter", + letter: "భ", + word: "సభ", + image: getAssetUrl(s3Assets.సభImg), + audio: getAssetAudioUrl(s3Assets.సభAudio), + singleAudio: getAssetAudioUrl(s3Assets.సభAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.భసభAudio), + }, + ], + }, + { + letter: "క్ష", + items: [ + { + id: 98, + title: "Letter", + letter: "క్ష", + word: "క్షత్రియుడు", + image: getAssetUrl(s3Assets.కషతరయడImg), + audio: getAssetAudioUrl(s3Assets.క్షక్షత్రియుడుAudio), + singleAudio: getAssetAudioUrl(s3Assets.క్షక్షత్రియుడుAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.క్షక్షత్రియుడుAudio), + }, + { + id: 99, + title: "Letter", + letter: "క్ష", + word: "అక్షరం", + image: getAssetUrl(s3Assets.అకషరImg), + audio: getAssetAudioUrl(s3Assets.అకషరAudio), + singleAudio: getAssetAudioUrl(s3Assets.అకషరAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.క్షఅక్షరంAudio), + }, + { + id: 100, + title: "Letter", + letter: "క్ష", + word: "పరీక్ష", + image: getAssetUrl(s3Assets.పరకషImg), + audio: getAssetAudioUrl(s3Assets.పరకషAudio), + singleAudio: getAssetAudioUrl(s3Assets.పరకషAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.క్షపరీక్షAudio), + }, + ], + }, + { + letter: "అం", + items: [ + { + id: 101, + title: "Letter", + letter: "అం", + word: "అంగడి", + image: getAssetUrl(s3Assets.అగడImg), + audio: getAssetAudioUrl(s3Assets.అంఅంగడిAudio), + singleAudio: getAssetAudioUrl(s3Assets.అంఅంగడిAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.అంఅంగడిAudio), + }, + ], + }, + { + letter: "ఙ", + items: [ + { + id: 102, + title: "Letter", + letter: "ఙ", + word: "ఙ", + image: getAssetUrl(s3Assets.ఙImg), + audio: getAssetAudioUrl(s3Assets.ఙఙAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఙఙAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఙఙAudio), + }, + ], + }, + { + letter: "ఞ", + items: [ + { + id: 103, + title: "Letter", + letter: "ఞ", + word: "ఞ", + image: getAssetUrl(s3Assets.ఞImg), + audio: getAssetAudioUrl(s3Assets.ఞఞAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఞఞAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఞఞAudio), + }, + ], + }, + { + letter: "అః", + items: [ + { + id: 104, + title: "Letter", + letter: "అః", + word: "అః", + image: getAssetUrl(s3Assets.అImg), + audio: getAssetAudioUrl(s3Assets.అఃఅఃAudio), + singleAudio: getAssetAudioUrl(s3Assets.అఃఅఃAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.అఃఅఃAudio), + }, + ], + }, + { + letter: "ఱ", + items: [ + { + id: 105, + title: "Letter", + letter: "ఱ", + word: "ఱంపం", + image: getAssetUrl(s3Assets.ఱపImg), + audio: getAssetAudioUrl(s3Assets.ఱఱంపంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఱఱంపంAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ఱఱంపంAudio), + }, + ], + }, + { + letter: "న", + items: [ + { + id: 106, + title: "Letter", + letter: "న", + word: "నగ", + image: getAssetUrl(s3Assets.నగImg), + audio: getAssetAudioUrl(s3Assets.ననగAudio), + singleAudio: getAssetAudioUrl(s3Assets.ననగAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ననగAudio), + }, + { + id: 107, + title: "Letter", + letter: "న", + word: "అనప", + image: getAssetUrl(s3Assets.అనపImg), + audio: getAssetAudioUrl(s3Assets.అనపSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.అనపSingleAudio), + }, + { + id: 108, + title: "Letter", + letter: "న", + word: "వాన", + image: getAssetUrl(s3Assets.వానImg), + audio: getAssetAudioUrl(s3Assets.వానSingleAudio), + singleAudio: getAssetAudioUrl(s3Assets.వానSingleAudio), + }, + ], + }, + { + letter: "ౠ", + items: [ + { + id: 109, + title: "Letter", + letter: "ౠ", + word: "ౠక", + image: getAssetUrl(s3Assets.ౠకImg), + audio: getAssetAudioUrl(s3Assets.ౠౠకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ౠౠకAudio), + alaphabetChartAudio: getAssetAudioUrl(s3Assets.ౠౠకAudio), + }, + ], + }, +]; + +const LetterTrain = ({ + isAlphabetDemoActive, + setVoiceText, + setRecordedAudio, + setVoiceAnimate, + storyLine, + type, + handleNext, + background, + parentWords = "", + //enableNext, + showTimer, + points, + steps, + currentStep, + contentId, + contentType, + level, + isDiscover, + progressData, + showProgress, + playTeacherAudio = () => {}, + callUpdateLearner, + disableScreen, + isShowCase, + handleBack, // This might be going to homepage + //setEnableNext, + loading, + setOpenMessageDialog, + audio, + currentImg, + vocabCount, + wordCount, + customLetters, // Array of letters to filter (e.g., ["a", "m", "s", "t"]) + confidentLetters, // Optional: Letters user is confident with (appear less frequently) + //isNextButtonCalled, + //setIsNextButtonCalled, +}) => { + steps = 1; + let lang = getLocalData("lang"); + // Normalize Kannada language codes: both "kn" and "ka" should work + if (lang === "ka") { + lang = "kn"; // Normalize to "kn" for consistency, but getFontFamily handles both + } + // Debug: Log language and font family + console.log( + "LetterTrain - Language:", + lang, + "Font Family:", + getFontFamily(lang) + ); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); + let data; + + const letterss = dataTe.flatMap((obj) => + obj.items.map((item) => item.letter) + ); + + const chunkSize = 5; + const result = []; + + for (let i = 0; i < letterss.length; i += chunkSize) { + result.push(letterss.slice(i, i + chunkSize)); + } + + console.log("===============================", result); + + if (lang === "en") { + data = dataEn; + } else if (lang === "hi") { + data = dataHi; + } else if (lang === "te") { + data = dataTe; + } else if (lang === "kn" || lang === "ka") { + data = dataKn; + } else { + data = dataEn; // fallback (English) + } + + // Filter data based on customLetters if provided + if ( + customLetters && + Array.isArray(customLetters) && + customLetters.length > 0 + ) { + // Normalize customLetters to uppercase for comparison + const normalizedCustomLetters = customLetters + .map((letter) => + letter && typeof letter === "string" ? letter.toUpperCase() : "" + ) + .filter(Boolean); + data = data.filter((letterObj) => { + // Check if the letter or syllable (uppercase) matches any of the custom letters + const letterToCheck = letterObj.letter || letterObj.syllable; + if (!letterToCheck || typeof letterToCheck !== "string") { + return false; + } + return normalizedCustomLetters.includes(letterToCheck.toUpperCase()); + }); + } + + const generatePlaylist = (data, confidentLettersList = []) => { + const playlist = []; + + // Normalize confident letters to uppercase for comparison + const normalizedConfident = confidentLettersList + .map((letter) => + letter && typeof letter === "string" ? letter.toUpperCase() : "" + ) + .filter(Boolean); + + for (let i = 0; i < data.length; i += 5) { + const block = data.slice(i, i + 5); + + block.forEach((letterObj) => { + if (letterObj.items && Array.isArray(letterObj.items)) { + const letterKey = ( + letterObj.letter || + letterObj.syllable || + "" + ).toUpperCase(); + const isConfident = normalizedConfident.includes(letterKey); + + // For confident letters: show only first item (reduced frequency) + // For non-confident letters: show all items (full practice) + const itemsToShow = isConfident + ? letterObj.items.slice(0, 1) // Only first item for confident letters + : letterObj.items; // All items for non-confident letters + + itemsToShow.forEach((item) => { + playlist.push({ + type: "UI1", + item, + letter: letterObj.letter || letterObj.syllable || "", + }); + }); + } + }); + + block.forEach((letterObj) => { + // Check if items exists, is an array, and has length > 0 + if ( + letterObj.items && + Array.isArray(letterObj.items) && + letterObj.items.length > 0 + ) { + const firstItem = letterObj.items[0]; + playlist.push({ + type: "UI2", + item: firstItem, + letter: letterObj.letter || letterObj.syllable || "", + }); + } + }); + } + + return playlist; + }; + + const playlist = generatePlaylist(data, confidentLetters || []); + console.log("LetterTrain playlist generated:", { + playlistLength: playlist.length, + customLetters, + confidentLetters, + confidentLettersCount: confidentLetters?.length || 0, + playlistItems: playlist.map((item, idx) => ({ + index: idx, + type: item.type, + letter: item.letter, + word: item.item?.word, + })), + }); + + const [currentIndex, setCurrentIndex] = useState(0); + const batchIndex = Math.floor(currentIndex / 10); + const stepInBatch = Math.floor((currentIndex % 10) / 5); + const itemIndex = batchIndex * 5 + (currentIndex % 5); + const item = playlist[currentIndex]?.item; + const prevItem = itemIndex > 0 ? data[itemIndex - 1] : null; + const blockStart = Math.floor(itemIndex / 5) * 5; + const currentLetter = item?.letter || ""; + const [letters, setLetters] = useState([]); + const COLORS = ["#8BC34A", "#9C27B0", "#E91E63", "#03A9F4", "#FF9800"]; + const [isRecordingComplete, setIsRecordingComplete] = useState(false); + const [recAudio, setRecAudio] = useState(null); + const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); + const [enableNext, setEnableNext] = useState(false); + const current = playlist[currentIndex]; + const navigate = useNavigate(); + const [open, setOpen] = useState(false); + + const audioRef = useRef(null); + + const currentAudio = + playlist[currentIndex]?.type === "UI2" + ? null + : playlist[currentIndex]?.item?.audio || null; + + const singleAudio = playlist[currentIndex]?.item?.singleAudio || null; + + const playAudio = (src) => { + if (!src) return; + + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + const audio = new Audio(src); + audioRef.current = audio; + + // audio.onended = () => { + // // Audio ended + // }; + + audio.play().catch((err) => { + console.log("Audio play error:", err); + }); + }; + + useEffect(() => { + if (currentAudio && !isAlphabetDemoActive) { + playAudio(currentAudio); + } + return () => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current = null; + } + }; + }, [currentIndex, isAlphabetDemoActive]); + + // const playAudio = (src) => { + // if (!src) return; + + // if (audioRef.current) { + // audioRef.current.pause(); + // audioRef.current.currentTime = 0; + // } + + // const audio = new Audio(src); + // audioRef.current = audio; + + // audio.play().catch((err) => { + // console.log("Audio play error:", err); + // }); + // }; + + // useEffect(() => { + // if (!currentAudio) return; + + // // 🔹 Special case: index 0 (only once, delayed) + // if (currentIndex === 0) { + // if (localStorage.getItem("alphabetdemo") === "true") return; + + // // Set immediately + // setLocalData("alphabetdemo", "true"); + // window.dispatchEvent(new Event("alphabetDemoComplete")); + + // const timeoutId = setTimeout(() => { + // playAudio(currentAudio); + // }, 6000); + + // return () => { + // clearTimeout(timeoutId); + // if (audioRef.current) { + // audioRef.current.pause(); + // audioRef.current = null; + // } + // }; + // } + + // // 🔹 Normal case: index 1 → 20 (play immediately) + // playAudio(currentAudio); + + // return () => { + // if (audioRef.current) { + // audioRef.current.pause(); + // audioRef.current = null; + // } + // }; + // }, [currentIndex]); + + const currentUI = useMemo(() => { + return playlist[currentIndex]?.type; + }, [currentIndex, playlist]); + + const handleNextWord = () => { + const currentLetter = + playlist[currentIndex]?.item?.letter || + playlist[currentIndex]?.item?.syllable || + ""; + + if (currentLetter && current.type === "UI1") { + setLetters((prev) => + prev.includes(currentLetter) ? prev : [...prev, currentLetter] + ); + } + + console.log("LetterTrain handleNextWord:", { + currentIndex, + playlistLength: playlist.length, + isLastItem: currentIndex >= playlist.length - 1, + customLetters, + hasHandleNext: !!handleNext, + }); + + // Check if we've reached the end of the playlist + if (currentIndex < playlist.length - 1) { + // Move to next item in playlist + setCurrentIndex((i) => i + 1); + } else { + // Reached end of playlist - complete the LetterTrain step + console.log( + "LetterTrain completed - all customLetters done. Calling handleNext." + ); + // If handleNext prop is provided (e.g., from F1), use it instead of default navigation + if (handleNext && typeof handleNext === "function") { + handleNext(); + } else { + // Default R0 behavior + setLocalData("rStepZero", 1); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + console.log("finished r0"); + } + // Don't continue - exit here + return; + } + setRecAudio(null); + setIsNextButtonCalled(true); + setEnableNext(false); + }; + + const handlePreviousWord = () => { + if (currentIndex > 0) { + const currentLetter = playlist[currentIndex]?.item?.letter || ""; + if (currentLetter && current.type === "UI1") { + setLetters((prev) => prev.filter((letter) => letter !== currentLetter)); + } + + setCurrentIndex((i) => i - 1); + setRecAudio(null); + setIsNextButtonCalled(false); + setEnableNext(false); + } + }; + + const handleBackNavigation = () => { + if (currentIndex > 0) { + handlePreviousWord(); + } else { + if (handleBack) { + handleBack(); + } else { + navigate(-1); + } + } + }; + + const handleRetry = () => { + console.log("audio playing!"); + playAudio(currentAudio); + }; + + const updateStoredData = (audio, isCorrect) => {}; + + const handleRecordingComplete = (base64Data) => { + if (base64Data) { + setIsRecordingComplete(true); + setRecAudio(base64Data); + } else { + setIsRecordingComplete(false); + setRecAudio(null); + } + }; + + const handleStartRecording = () => { + setRecAudio(null); + }; + + const handleStopRecording = () => { + setRecAudio(true); + setLetters([]); + }; + + const navy = "#1c2752"; + const red = "#C93128"; + const pink = "#ea4c89"; + const orange = "#f28b1d"; + const blue = "#f28b1d"; + + const flowNames = [...new Set(data.map((item) => item.id))]; + + const renderUI = () => { + const cycleIndex = Math.floor(currentIndex / 20); + const positionInCycle = currentIndex % 20; + + const current = playlist[currentIndex]; + if (!current) return null; + + //console.log('ui?', currentIndex, block, isUI1, letters); + + const totalLetters = data && Array.isArray(data) ? data.length : 0; + const completedLetters = + letters && Array.isArray(letters) ? letters.length : 0; + + // Calculate total items in playlist + const totalItemsInPlaylist = + playlist && Array.isArray(playlist) ? playlist.length : 0; + + const completionPercentage = + totalLetters > 0 + ? Math.round((completedLetters / totalLetters) * 100) + : 0; + const UI1 = () => { + //console.log("ui1", item, current); + + const renderHighlightedWord = (word, targetLetter) => { + if (!word || !targetLetter) return word; + if (lang !== "en") { + const graphemes = splitGraphemes(word); + const graphemeIndex = graphemes.findIndex((g) => + g.includes(targetLetter) + ); + if (graphemeIndex === -1) { + return word; + } + const before = graphemes.slice(0, graphemeIndex).join(""); + const letter = graphemes[graphemeIndex]; + const after = graphemes.slice(graphemeIndex + 1).join(""); + return ( + <> + {before} + + {letter} + + {after} + + ); + } + + const lowerWord = word.toLowerCase(); + const lowerTarget = targetLetter.toLowerCase(); + + const letterIndex = lowerWord.indexOf(lowerTarget); + + if (letterIndex === -1) { + return word; + } + + const before = word.substring(0, letterIndex); + const letter = word.substring( + letterIndex, + letterIndex + targetLetter.length + ); + const after = word.substring(letterIndex + targetLetter.length); + + return ( + <> + {before} + + {letter} + + {after} + + ); + }; + + let TOTAL_ITEMS = 0; + + // Use actual playlist length if customLetters is provided, otherwise use hardcoded values + if ( + customLetters && + Array.isArray(customLetters) && + customLetters.length > 0 && + playlist && + Array.isArray(playlist) + ) { + TOTAL_ITEMS = playlist.length; + } else { + if (lang === "en") { + TOTAL_ITEMS = 101; + } else if (lang === "hi") { + TOTAL_ITEMS = 151; + } else if (lang === "te") { + TOTAL_ITEMS = 146; + } else if (lang === "kn" || lang === "ka") { + TOTAL_ITEMS = 142; + } else { + TOTAL_ITEMS = 100; // fallback default + } + } + + // FIXED: Use currentIndex + 1 instead of item?.id + const currentItemNumber = currentIndex + 1; + const completionPercentage = Math.round( + (currentItemNumber / TOTAL_ITEMS) * 100 + ); + + return ( + + + {/* Progress container - right side */} + + + {currentItemNumber}/{TOTAL_ITEMS} + + + + + + + + {/* Title container - left side */} + + + {item.title} + + + + + train + + + + {letters?.map((ch, i) => ( + + + + {ch} + + + + ))} + + + + + + + {item.letters && + Array.isArray(item.letters) && + item.letters.length > 1 ? ( + <> + {item.letters[0]} + {item.letters[1]} + {item.letters.slice(2)} + + ) : ( + + {item.letters || item.letter || item.syllable || ""} + + )} + + + + + + + + + + + + {renderHighlightedWord(item.word, item.syllable || item.letter)} + + + + + + + + + + + + + {/* ➡️ Next button */} + + + + + + + ); + }; + const UI2 = () => { + //console.log("ui2"); + + let TOTAL_ITEMS = 0; + // Use actual playlist length if customLetters is provided, otherwise use hardcoded values + if ( + customLetters && + Array.isArray(customLetters) && + customLetters.length > 0 + ) { + TOTAL_ITEMS = playlist.length; + } else { + if (lang === "en") TOTAL_ITEMS = 101; + else if (lang === "kn" || lang === "ka") TOTAL_ITEMS = 142; + else if (lang === "hi") TOTAL_ITEMS = 151; + else if (lang === "te") TOTAL_ITEMS = 146; + else TOTAL_ITEMS = 100; // fallback + } + const currentItemNumber = currentIndex + 1; + const completionPercentage = Math.round( + (currentItemNumber / TOTAL_ITEMS) * 100 + ); + + const renderHighlightedWord = (word, targetLetter) => { + if (!word || !targetLetter) return word; + + const lowerWord = word.toLowerCase(); + const lowerTarget = targetLetter.toLowerCase(); + + const letterIndex = lowerWord.indexOf(lowerTarget); + + if (letterIndex === -1) { + return word; + } + + const before = word.substring(0, letterIndex); + const letter = word.substring( + letterIndex, + letterIndex + targetLetter.length + ); + const after = word.substring(letterIndex + targetLetter.length); + + return ( + <> + {before} + + {letter} + + {after} + + ); + }; + + return ( + + + + + {currentItemNumber}/{TOTAL_ITEMS} + + + + + + + + + + + {renderHighlightedWord( + item.word, + item.syllable || item.letter + )} + + + + {recAudio && ( + + graph + + )} + + + + + + + + ); + }; + if (current.type === "UI1") { + return UI1(current.item); + } else { + return UI2(current.item); + } + }; + + return ( + + + hint setOpen(true)} + /> + + {/* Modal */} + {open && ( +
+
+ {/* Close Button */} + + + {/* YouTube Video */} + +
+
+ )} + {isAlphabetDemoActive ? ( + + + + ) : ( + renderUI() + )} +
+
+ ); +}; + +export default LetterTrain; diff --git a/src/RFlow/R0.jsx b/src/RFlow/R0.jsx index ec3ad541..f3ea3232 100644 --- a/src/RFlow/R0.jsx +++ b/src/RFlow/R0.jsx @@ -22,6 +22,7 @@ import { RetryIcon, setLocalData, } from "../utils/constants"; +import { getFontFamily } from "../utils/fontUtils"; import { useNavigate } from "react-router-dom"; import { response } from "../services/telementryService"; import { Typography, Stack, IconButton } from "@mui/material"; @@ -930,9 +931,9 @@ const dataKn = [ letters: "ಅ", letter: "ಅ", word: "ಅರಸ", - image: getAssetUrl(s3Assets.kingAlpTelImage), - audio: getAssetAudioUrl(s3Assets.kingAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.kingAlpAudio), + image: getAssetUrl(s3Assets.ಅರಸImg), + audio: getAssetAudioUrl(s3Assets.ಅರಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಅರಸSingleAudio), }, ], }, @@ -945,9 +946,9 @@ const dataKn = [ letters: "ಆ", letter: "ಆ", word: "ಆನೆ", - image: getAssetUrl(s3Assets.elephentAlpTelImage), - audio: getAssetAudioUrl(s3Assets.elephantAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.elephantAlpAudio), + image: getAssetUrl(s3Assets.ಆನೆImg), + audio: getAssetAudioUrl(s3Assets.ಆನೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆನೆSingleAudio), }, ], }, @@ -960,9 +961,9 @@ const dataKn = [ letters: "ಇ", letter: "ಇ", word: "ಇಲಿ", - image: getAssetUrl(s3Assets.ratAlpTelImage), - audio: getAssetAudioUrl(s3Assets.mouseAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.mouseAlpAudio), + image: getAssetUrl(s3Assets.ಇಲಿImg), + audio: getAssetAudioUrl(s3Assets.ಇಲಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಇಲಿSingleAudio), }, ], }, @@ -975,9 +976,9 @@ const dataKn = [ letters: "ಈ", letter: "ಈ", word: "ಈಜು", - image: getAssetUrl(s3Assets.swimAlpTelImage), - audio: getAssetAudioUrl(s3Assets.swimAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.swimAlpAudio), + image: getAssetUrl(s3Assets.ಈಜುImg), + audio: getAssetAudioUrl(s3Assets.ಈಜುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಈಜುSingleAudio), }, ], }, @@ -990,9 +991,9 @@ const dataKn = [ letters: "ಉ", letter: "ಉ", word: "ಉದರ", - image: getAssetUrl(s3Assets.bellyAlpTelImage), - audio: getAssetAudioUrl(s3Assets.bellyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.bellyAlpAudio), + image: getAssetUrl(s3Assets.ಉದರImg), + audio: getAssetAudioUrl(s3Assets.ಉದರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಉದರSingleAudio), }, ], }, @@ -1005,9 +1006,9 @@ const dataKn = [ letters: "ಊ", letter: "ಊ", word: "ಊಟ", - image: getAssetUrl(s3Assets.foodAlpTelImage), - audio: getAssetAudioUrl(s3Assets.mealAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.mealAlpAudio), + image: getAssetUrl(s3Assets.ಊಟImg), + audio: getAssetAudioUrl(s3Assets.ಊಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಊಟSingleAudio), }, ], }, @@ -1020,9 +1021,9 @@ const dataKn = [ letters: "ಋ", letter: "ಋ", word: "ಋಷಿ", - image: getAssetUrl(s3Assets.monkAlpTelImage), - audio: getAssetAudioUrl(s3Assets.sageAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.sageAlpAudio), + image: getAssetUrl(s3Assets.ಋಷಿImg), + audio: getAssetAudioUrl(s3Assets.ಋಷಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಋಷಿSingleAudio), }, ], }, @@ -1035,9 +1036,9 @@ const dataKn = [ letters: "ಎ", letter: "ಎ", word: "ಎಲೆ", - image: getAssetUrl(s3Assets.leafAlpTelImage), - audio: getAssetAudioUrl(s3Assets.leafAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.leafAlpAudio), + image: getAssetUrl(s3Assets.ಎಲೆImg), + audio: getAssetAudioUrl(s3Assets.ಎಲೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಎಲೆSingleAudio), }, ], }, @@ -1050,9 +1051,9 @@ const dataKn = [ letters: "ಏ", letter: "ಏ", word: "ಏಣಿ", - image: getAssetUrl(s3Assets.stairAlpTelImage), - audio: getAssetAudioUrl(s3Assets.ladderAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.ladderAlpAudio), + image: getAssetUrl(s3Assets.ಏಣಿImg), + audio: getAssetAudioUrl(s3Assets.ಏಣಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಏಣಿSingleAudio), }, ], }, @@ -1065,9 +1066,9 @@ const dataKn = [ letters: "ಐ", letter: "ಐ", word: "ಐದು", - image: getAssetUrl(s3Assets.fiveAlpTelImage), - audio: getAssetAudioUrl(s3Assets.fiveAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.fiveAlpAudio), + image: getAssetUrl(s3Assets.ಐದುImg), + audio: getAssetAudioUrl(s3Assets.ಐದುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಐದುSingleAudio), }, ], }, @@ -1080,9 +1081,9 @@ const dataKn = [ letters: "ಒ", letter: "ಒ", word: "ಒಂಟೆ", - image: getAssetUrl(s3Assets.camelAlpTelImage), - audio: getAssetAudioUrl(s3Assets.camelAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.camelAlpAudio), + image: getAssetUrl(s3Assets.ಒಂಟೆImg), + audio: getAssetAudioUrl(s3Assets.ಒಂಟೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಒಂಟೆSingleAudio), }, ], }, @@ -1095,9 +1096,9 @@ const dataKn = [ letters: "ಓ", letter: "ಓ", word: "ಓಡು", - image: getAssetUrl(s3Assets.runAlpTelImage), - audio: getAssetAudioUrl(s3Assets.runningAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.runningAlpAudio), + image: getAssetUrl(s3Assets.ಓಡುImg), + audio: getAssetAudioUrl(s3Assets.ಓಡುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಓಡುSingleAudio), }, ], }, @@ -1110,44 +1111,74 @@ const dataKn = [ letters: "ಔ", letter: "ಔ", word: "ಔಷಧ", - image: getAssetUrl(s3Assets.medicineAlpTelImage), - audio: getAssetAudioUrl(s3Assets.medicineAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.medicineAlpAudio), + image: getAssetUrl(s3Assets.ಔಷಧImg), + audio: getAssetAudioUrl(s3Assets.ಔಷಧAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಔಷಧSingleAudio), }, ], }, { - letter: "ಕ", + letter: "ಅಂ", items: [ { id: 14, + title: "ಸ್ವರಗಳು", + letters: "ಅಂ", + letter: "ಅಂ", + word: "ಅಂಗಡಿ", + image: getAssetUrl(s3Assets.ಅಂಗಡಿImg), + audio: getAssetAudioUrl(s3Assets.ಅಂಗಡಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಅಂಗಡಿSingleAudio), + }, + ], + }, + { + letter: "ಅಃ", + items: [ + { + id: 15, + title: "ಸ್ವರಗಳು", + letters: "ಅಃ", + letter: "ಅಃ", + word: "ಅಃ", + image: getAssetUrl(s3Assets.ಅಃImg), + audio: getAssetAudioUrl(s3Assets.ಅಃAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಅಃSingleAudio), + }, + ], + }, + { + letter: "ಕ", + items: [ + { + id: 16, title: "ವ್ಯಂಜನಗಳು", letters: "ಕ", letter: "ಕ", word: "ಕಮಲ", - image: getAssetUrl(s3Assets.lotusAlpTelImage), - audio: getAssetAudioUrl(s3Assets.lotusAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.lotusAlpAudio), + image: getAssetUrl(s3Assets.ಕಮಲImg), + audio: getAssetAudioUrl(s3Assets.ಕಮಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಮಲSingleAudio), }, { - id: 15, + id: 17, title: "ವ್ಯಂಜನಗಳು", letters: "ಕ", letter: "ಕ", word: "ಏಕದಳ", - image: getAssetUrl(s3Assets.cerealAlpTelImage), - audio: getAssetAudioUrl(s3Assets.seedCornGrainAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.seedCornGrainAlpAudio), + image: getAssetUrl(s3Assets.ಏಕದಳImg), + audio: getAssetAudioUrl(s3Assets.ಏಕದಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಏಕದಳSingleAudio), }, { - id: 16, + id: 18, title: "ವ್ಯಂಜನಗಳು", letters: "ಕ", letter: "ಕ", word: "ಪದಕ", - image: getAssetUrl(s3Assets.medalAlpTelImage), - audio: getAssetAudioUrl(s3Assets.medalAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.medalAlpAudio), + image: getAssetUrl(s3Assets.ಪದಕImg), + audio: getAssetAudioUrl(s3Assets.ಪದಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪದಕSingleAudio), }, ], }, @@ -1155,34 +1186,34 @@ const dataKn = [ letter: "ಖ", items: [ { - id: 17, + id: 19, title: "ವ್ಯಂಜನಗಳು", letters: "ಖ", letter: "ಖ", word: "ಖಡ್ಗ", - image: getAssetUrl(s3Assets.swordAlpTelImage), - audio: getAssetAudioUrl(s3Assets.swordAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.swordAlpAudio), + image: getAssetUrl(s3Assets.ಖಡ್ಗImg), + audio: getAssetAudioUrl(s3Assets.ಖಡ್ಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಖಡ್ಗSingleAudio), }, { - id: 18, + id: 20, title: "ವ್ಯಂಜನಗಳು", letters: "ಖ", letter: "ಖ", word: "ಲೇಖನಿ", - image: getAssetUrl(s3Assets.penAlpTelImage), - audio: getAssetAudioUrl(s3Assets.penAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.penAlpAudio), + image: getAssetUrl(s3Assets.ಲೇಖನಿImg), + audio: getAssetAudioUrl(s3Assets.ಲೇಖನಿ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಲೇಖನಿSingleAudio), }, { - id: 19, + id: 21, title: "ವ್ಯಂಜನಗಳು", letters: "ಖ", letter: "ಖ", word: "ಪಂಖ", - image: getAssetUrl(s3Assets.fanAlpTelImage), - audio: getAssetAudioUrl(s3Assets.fanAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.fanAlpAudio), + image: getAssetUrl(s3Assets.ಪಂಖImg), + audio: getAssetAudioUrl(s3Assets.ಪಂಖAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪಂಖSingleAudio), }, ], }, @@ -1190,34 +1221,34 @@ const dataKn = [ letter: "ಗ", items: [ { - id: 20, + id: 22, title: "ವ್ಯಂಜನಗಳು", letters: "ಗ", letter: "ಗ", word: "ಗರಿ", - image: getAssetUrl(s3Assets.featherAlpTelImage), - audio: getAssetAudioUrl(s3Assets.featherAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.featherAlpAudio), + image: getAssetUrl(s3Assets.ಗರಿImg), + audio: getAssetAudioUrl(s3Assets.ಗರಿ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಗರಿSingleAudio), }, { - id: 21, + id: 23, title: "ವ್ಯಂಜನಗಳು", letters: "ಗ", letter: "ಗ", word: "ಆಗಸ", - image: getAssetUrl(s3Assets.skyAlpTelImage), - audio: getAssetAudioUrl(s3Assets.skyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.skyAlpAudio), + image: getAssetUrl(s3Assets.ಆಗಸImg), + audio: getAssetAudioUrl(s3Assets.ಆಗಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಗಸSingleAudio), }, { - id: 22, + id: 24, title: "ವ್ಯಂಜನಗಳು", letters: "ಗ", letter: "ಗ", word: "ಉರಗ", - image: getAssetUrl(s3Assets.snakeAlpTelImage), - audio: getAssetAudioUrl(s3Assets.snakeAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.snakeAlpAudio), + image: getAssetUrl(s3Assets.ಉರಗImg), + audio: getAssetAudioUrl(s3Assets.ಉರಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಉರಗSingleAudio), }, ], }, @@ -1225,34 +1256,49 @@ const dataKn = [ letter: "ಘ", items: [ { - id: 23, + id: 25, title: "ವ್ಯಂಜನಗಳು", letters: "ಘ", letter: "ಘ", word: "ಘಂಟೆ", - image: getAssetUrl(s3Assets.drumAlpTelImage), - audio: getAssetAudioUrl(s3Assets.bellAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.bellAlpAudio), + image: getAssetUrl(s3Assets.ಘಂಟೆImg), + audio: getAssetAudioUrl(s3Assets.ಘಂಟೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಘಂಟೆSingleAudio), }, { - id: 24, + id: 26, title: "ವ್ಯಂಜನಗಳು", letters: "ಘ", letter: "ಘ", word: "ಘಮಘಮ", - image: getAssetUrl(s3Assets.fragranceAlpTelImage), - audio: getAssetAudioUrl(s3Assets.fragranceAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.fragranceAlpAudio), + image: getAssetUrl(s3Assets.ಘಮಘಮImg), + audio: getAssetAudioUrl(s3Assets.ಘಮಘಮAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಘಮಘಮSingleAudio), }, { - id: 25, + id: 27, title: "ವ್ಯಂಜನಗಳು", letters: "ಘ", letter: "ಘ", word: "ಸಂಘ", - image: getAssetUrl(s3Assets.childrenassociationAlpTelImage), - audio: getAssetAudioUrl(s3Assets.associationAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.associationAlpAudio), + image: getAssetUrl(s3Assets.ಸಂಘImg), + audio: getAssetAudioUrl(s3Assets.ಸಂಘAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸಂಘSingleAudio), + }, + ], + }, + { + letter: "ಙ", + items: [ + { + id: 28, + title: "ವ್ಯಂಜನಗಳು", + letters: "ಙ", + letter: "ಙ", + word: "ಙ", + image: getAssetUrl(s3Assets.ಙImg), + audio: getAssetAudioUrl(s3Assets.ಙAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಙSingleAudio), }, ], }, @@ -1260,34 +1306,49 @@ const dataKn = [ letter: "ಚ", items: [ { - id: 26, + id: 29, title: "ವ್ಯಂಜನಗಳು", letters: "ಚ", letter: "ಚ", word: "ಚಮಚ", - image: getAssetUrl(s3Assets.spoonAlpTelImage), - audio: getAssetAudioUrl(s3Assets.spoonAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.spoonAlpAudio), + image: getAssetUrl(s3Assets.ಚಮಚImg), + audio: getAssetAudioUrl(s3Assets.ಚಮಚAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಚಮಚSingleAudio), }, { - id: 27, + id: 30, title: "ವ್ಯಂಜನಗಳು", letters: "ಚ", letter: "ಚ", word: "ಈಚಲ", - image: getAssetUrl(s3Assets.treeAlpTelImage), - audio: getAssetAudioUrl(s3Assets.palmAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.palmAlpAudio), + image: getAssetUrl(s3Assets.ಈಚಲImg), + audio: getAssetAudioUrl(s3Assets.ಈಚಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಈಚಲSingleAudio), }, { - id: 28, + id: 31, title: "ವ್ಯಂಜನಗಳು", letters: "ಚ", letter: "ಚ", word: "ಮಂಚ", - image: getAssetUrl(s3Assets.couchAlpTelImage), - audio: getAssetAudioUrl(s3Assets.cotAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.cotAlpAudio), + image: getAssetUrl(s3Assets.ಮಂಚImg), + audio: getAssetAudioUrl(s3Assets.ಮಂಚAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಮಂಚSingleAudio), + }, + ], + }, + { + letter: "ಛ", + items: [ + { + id: 32, + title: "ವ್ಯಂಜನಗಳು", + letters: "ಛ", + letter: "ಛ", + word: "ಛತ್ರಿ", + image: getAssetUrl(s3Assets.ಛತ್ರಿImg), + audio: getAssetAudioUrl(s3Assets.ಛತ್ರಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಛತ್ರಿSingleAudio), }, ], }, @@ -1295,34 +1356,64 @@ const dataKn = [ letter: "ಜ", items: [ { - id: 29, + id: 33, title: "ವ್ಯಂಜನಗಳು", letters: "ಜ", letter: "ಜ", word: "ಜನ", - image: getAssetUrl(s3Assets.peopleAlpTelImage), - audio: getAssetAudioUrl(s3Assets.peopleAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.peopleAlpAudio), + image: getAssetUrl(s3Assets.ಜನImg), + audio: getAssetAudioUrl(s3Assets.ಜನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಜನSingleAudio), }, { - id: 30, + id: 34, title: "ವ್ಯಂಜನಗಳು", letters: "ಜ", letter: "ಜ", word: "ಗೀಜಗ", - image: getAssetUrl(s3Assets.birdAlpTelImage), - audio: getAssetAudioUrl(s3Assets.weaverbirdAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.weaverbirdAlpAudio), + image: getAssetUrl(s3Assets.ಗೀಜಗImg), + audio: getAssetAudioUrl(s3Assets.ಗೀಜಗAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗೀಜಗSingleAudio), }, { - id: 31, + id: 35, title: "ವ್ಯಂಜನಗಳು", letters: "ಜ", letter: "ಜ", word: "ಭುಜ", - image: getAssetUrl(s3Assets.shoulderAlpTelImage), - audio: getAssetAudioUrl(s3Assets.shoulderAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.shoulderAlpAudio), + image: getAssetUrl(s3Assets.ಭುಜImg), + audio: getAssetAudioUrl(s3Assets.ಭುಜAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಭುಜSingleAudio), + }, + ], + }, + { + letter: "ಝ", + items: [ + { + id: 36, + title: "ವ್ಯಂಜನಗಳು", + letters: "ಝ", + letter: "ಝ", + word: "ಝರಿ", + image: getAssetUrl(s3Assets.ಝರಿImg), + audio: getAssetAudioUrl(s3Assets.ಝರಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಝರಿSingleAudio), + }, + ], + }, + { + letter: "ಞ", + items: [ + { + id: 37, + title: "ವ್ಯಂಜನಗಳು", + letters: "ಞ", + letter: "ಞ", + word: "ಞ", + image: getAssetUrl(s3Assets.ಞImg), + audio: getAssetAudioUrl(s3Assets.ಞAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಞSingleAudio), }, ], }, @@ -1330,34 +1421,34 @@ const dataKn = [ letter: "ಟ", items: [ { - id: 32, + id: 38, title: "ವ್ಯಂಜನಗಳು", letters: "ಟ", letter: "ಟ", word: "ಟಗರು", - image: getAssetUrl(s3Assets.sheepAlpTelImage), - audio: getAssetAudioUrl(s3Assets.maleSheepAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.maleSheepAlpAudio), + image: getAssetUrl(s3Assets.ಟಗರುImg), + audio: getAssetAudioUrl(s3Assets.ಟಗರುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಟಗರುSingleAudio), }, { - id: 33, + id: 39, title: "ವ್ಯಂಜನಗಳು", letters: "ಟ", letter: "ಟ", word: "ಕಿಟಕಿ", - image: getAssetUrl(s3Assets.windowAlpTelImage), - audio: getAssetAudioUrl(s3Assets.windowAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.windowAlpAudio), + image: getAssetUrl(s3Assets.ಕಿಟಕಿImg), + audio: getAssetAudioUrl(s3Assets.ಕಿಟಕಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಿಟಕಿSingleAudio), }, { - id: 34, + id: 40, title: "ವ್ಯಂಜನಗಳು", letters: "ಟ", letter: "ಟ", word: "ಆಟ", - image: getAssetUrl(s3Assets.weplayAlpTelImage), - audio: getAssetAudioUrl(s3Assets.playAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.playAlpAudio), + image: getAssetUrl(s3Assets.ಆಟImg), + audio: getAssetAudioUrl(s3Assets.ಆಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಟSingleAudio), }, ], }, @@ -1365,24 +1456,34 @@ const dataKn = [ letter: "ಠ", items: [ { - id: 35, + id: 41, + title: "ವ್ಯಂಜನಗಳು", + letters: "ಠ", + letter: "ಠ", + word: "ಠಕ್ಕ", + image: getAssetUrl(s3Assets.ಠಕ್ಕImg), + audio: getAssetAudioUrl(s3Assets.ಠಕ್ಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಠಕ್ಕSingleAudio), + }, + { + id: 42, title: "ವ್ಯಂಜನಗಳು", letters: "ಠ", letter: "ಠ", word: "ಕೊಠಡಿ", - image: getAssetUrl(s3Assets.bedroomAlpTelImage), - audio: getAssetAudioUrl(s3Assets.roomAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.roomAlpAudio), + image: getAssetUrl(s3Assets.ಕೊಠಡಿImg), + audio: getAssetAudioUrl(s3Assets.ಕೊಠಡಿ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಕೊಠಡಿSingleAudio), }, { - id: 36, + id: 43, title: "ವ್ಯಂಜನಗಳು", letters: "ಠ", letter: "ಠ", word: "ಕಂಠ", - image: getAssetUrl(s3Assets.neckAlpTelImage), - audio: getAssetAudioUrl(s3Assets.frontPartOfTheNeckAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.frontPartOfTheNeckAlpAudio), + image: getAssetUrl(s3Assets.ಕಂಠImg), + audio: getAssetAudioUrl(s3Assets.ಕಂಠAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಂಠSingleAudio), }, ], }, @@ -1390,34 +1491,34 @@ const dataKn = [ letter: "ಡ", items: [ { - id: 37, + id: 44, title: "ವ್ಯಂಜನಗಳು", letters: "ಡ", letter: "ಡ", - word: "ಡಬ್ಬಿ", - image: getAssetUrl(s3Assets.boxAlpTelImage), - audio: getAssetAudioUrl(s3Assets.smallBoxOrChestAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.smallBoxOrChestAlpAudio), + word: "ಡಬ್ಬ", + image: getAssetUrl(s3Assets.ಡಬ್ಬImg), + audio: getAssetAudioUrl(s3Assets.ಡಬ್ಬAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಡಬ್ಬSingleAudio), }, { - id: 38, + id: 45, title: "ವ್ಯಂಜನಗಳು", letters: "ಡ", letter: "ಡ", word: "ಕಡಲು", - image: getAssetUrl(s3Assets.beachAlpTelImage), - audio: getAssetAudioUrl(s3Assets.oceanAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.oceanAlpAudio), + image: getAssetUrl(s3Assets.ಕಡಲುImg), + audio: getAssetAudioUrl(s3Assets.ಕಡಲುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಡಲುSingleAudio), }, { - id: 39, + id: 46, title: "ವ್ಯಂಜನಗಳು", letters: "ಡ", letter: "ಡ", word: "ಗಿಡ", - image: getAssetUrl(s3Assets.plantAlpTelImage), - audio: getAssetAudioUrl(s3Assets.plantAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.plantAlpAudio), + image: getAssetUrl(s3Assets.ಗಿಡImg), + audio: getAssetAudioUrl(s3Assets.ಗಿಡAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಿಡSingleAudio), }, ], }, @@ -1425,34 +1526,34 @@ const dataKn = [ letter: "ಢ", items: [ { - id: 40, + id: 47, title: "ವ್ಯಂಜನಗಳು", letters: "ಢ", letter: "ಢ", word: "ಢಣಢಣ", - image: getAssetUrl(s3Assets.drumAlpTelImage), - audio: getAssetAudioUrl(s3Assets.manRingingTheBellAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.manRingingTheBellAlpAudio), + image: getAssetUrl(s3Assets.ಢಣಢಣImg), + audio: getAssetAudioUrl(s3Assets.ಢಣಢಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಢಣಢಣSingleAudio), }, { - id: 41, + id: 48, title: "ವ್ಯಂಜನಗಳು", letters: "ಢ", letter: "ಢ", word: "ಪ್ರೌಢಶಾಲೆ", - image: getAssetUrl(s3Assets.schoolAlpTelImage), - audio: getAssetAudioUrl(s3Assets.highschoolAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.highschoolAlpAudio), + image: getAssetUrl(s3Assets.ಪ್ರೌಢಶಾಲೆImg), + audio: getAssetAudioUrl(s3Assets.ಪ್ರೌಢಶಾಲೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪ್ರೌಢಶಾಲೆSingleAudio), }, { - id: 42, + id: 49, title: "ವ್ಯಂಜನಗಳು", letters: "ಢ", letter: "ಢ", word: "ಗಾಢ", - image: getAssetUrl(s3Assets.eggFiveImg), - audio: getAssetAudioUrl(s3Assets.gaadhaNoImagAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.gaadhaNoImagAlpAudio), + image: getAssetUrl(s3Assets.ಗಾಢImg), + audio: getAssetAudioUrl(s3Assets.ಗಾಢAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಾಢSingleAudio), }, ], }, @@ -1460,24 +1561,24 @@ const dataKn = [ letter: "ಣ", items: [ { - id: 43, + id: 50, title: "ವ್ಯಂಜನಗಳು", letters: "ಣ", letter: "ಣ", - word: "ಹಣತೆ", - image: getAssetUrl(s3Assets.candleAlpTelImage), - audio: getAssetAudioUrl(s3Assets.earthenLampDiyaAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.earthenLampDiyaAlpAudio), + word: "ಹಣ", + image: getAssetUrl(s3Assets.ಹಣImg), + audio: getAssetAudioUrl(s3Assets.ಹಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಣSingleAudio), }, { - id: 44, + id: 51, title: "ವ್ಯಂಜನಗಳು", letters: "ಣ", letter: "ಣ", - word: "ಹಣ", - image: getAssetUrl(s3Assets.coinAlpTelImage), - audio: getAssetAudioUrl(s3Assets.moneyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.moneyAlpAudio), + word: "ಹಣತೆ", + image: getAssetUrl(s3Assets.ಹಣತೆImg), + audio: getAssetAudioUrl(s3Assets.ಹಣತೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಣತೆSingleAudio), }, ], }, @@ -1485,34 +1586,34 @@ const dataKn = [ letter: "ತ", items: [ { - id: 45, + id: 52, title: "ವ್ಯಂಜನಗಳು", letters: "ತ", letter: "ತ", word: "ತಬಲ", - image: getAssetUrl(s3Assets.tabalaAlpTelImage), - audio: getAssetAudioUrl(s3Assets.tabalaAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.tabalaAlpAudio), + image: getAssetUrl(s3Assets.ತಬಲImg), + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ತಬಲSingleAudio), }, { - id: 46, + id: 53, title: "ವ್ಯಂಜನಗಳು", letters: "ತ", letter: "ತ", word: "ಸಂತಸ", - image: getAssetUrl(s3Assets.happyAlpTelImage), - audio: getAssetAudioUrl(s3Assets.joyHappyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.joyHappyAlpAudio), + image: getAssetUrl(s3Assets.ಸಂತಸImg), + audio: getAssetAudioUrl(s3Assets.ಸಂತಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸಂತಸSingleAudio), }, { - id: 47, + id: 54, title: "ವ್ಯಂಜನಗಳು", letters: "ತ", letter: "ತ", word: "ಗಣಿತ", - image: getAssetUrl(s3Assets.mathematicsAlpTelImage), - audio: getAssetAudioUrl(s3Assets.mathAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.mathAlpAudio), + image: getAssetUrl(s3Assets.ಗಣಿತImg), + audio: getAssetAudioUrl(s3Assets.ಗಣಿತAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಣಿತSingleAudio), }, ], }, @@ -1520,34 +1621,34 @@ const dataKn = [ letter: "ಥ", items: [ { - id: 48, + id: 55, title: "ವ್ಯಂಜನಗಳು", letters: "ಥ", letter: "ಥ", word: "ಥಳಥಳ", - image: getAssetUrl(s3Assets.necklaceAlpTelImage), - audio: getAssetAudioUrl(s3Assets.shiningNecklaceAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.shiningNecklaceAlpAudio), + image: getAssetUrl(s3Assets.ಥಳಥಳImg), + audio: getAssetAudioUrl(s3Assets.ಥಳಥಳAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಥಳಥಳSingleAudio), }, { - id: 49, + id: 56, title: "ವ್ಯಂಜನಗಳು", letters: "ಥ", letter: "ಥ", word: "ಥರಥರ", - image: getAssetUrl(s3Assets.fearAlpTelImage), - audio: getAssetAudioUrl(s3Assets.shakingWithFearAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.shakingWithFearAlpAudio), + image: getAssetUrl(s3Assets.ಥರಥರImg), + audio: getAssetAudioUrl(s3Assets.ಥರಥರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಥರಥರSingleAudio), }, { - id: 50, + id: 57, title: "ವ್ಯಂಜನಗಳು", letters: "ಥ", letter: "ಥ", word: "ರಥ", - image: getAssetUrl(s3Assets.horsechariotAlpTelImage), - audio: getAssetAudioUrl(s3Assets.chariotAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.chariotAlpAudio), + image: getAssetUrl(s3Assets.ರಥImg), + audio: getAssetAudioUrl(s3Assets.ರಥAudio), + singleAudio: getAssetAudioUrl(s3Assets.ರಥSingleAudio), }, ], }, @@ -1555,34 +1656,34 @@ const dataKn = [ letter: "ದ", items: [ { - id: 51, + id: 58, title: "ವ್ಯಂಜನಗಳು", letters: "ದ", letter: "ದ", word: "ದನ", - image: getAssetUrl(s3Assets.cowAlpTelImage), - audio: getAssetAudioUrl(s3Assets.cowAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.cowAlpAudio), + image: getAssetUrl(s3Assets.ದನImg), + audio: getAssetAudioUrl(s3Assets.ದನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ದನSingleAudio), }, { - id: 52, + id: 59, title: "ವ್ಯಂಜನಗಳು", letters: "ದ", letter: "ದ", word: "ಕೂದಲು", - image: getAssetUrl(s3Assets.hairAlpTelImage), - audio: getAssetAudioUrl(s3Assets.hairAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.hairAlpAudio), + image: getAssetUrl(s3Assets.ಕೂದಲುImg), + audio: getAssetAudioUrl(s3Assets.ಕೂದಲುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕೂದಲುSingleAudio), }, { - id: 53, + id: 60, title: "ವ್ಯಂಜನಗಳು", letters: "ದ", letter: "ದ", word: "ಕಾಗದ", - image: getAssetUrl(s3Assets.paperAlpTelImage), - audio: getAssetAudioUrl(s3Assets.paperAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.paperAlpAudio), + image: getAssetUrl(s3Assets.ಕಾಗದImg), + audio: getAssetAudioUrl(s3Assets.ಕಾಗದAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಾಗದSingleAudio), }, ], }, @@ -1590,34 +1691,34 @@ const dataKn = [ letter: "ಧ", items: [ { - id: 54, + id: 61, title: "ವ್ಯಂಜನಗಳು", letters: "ಧ", letter: "ಧ", word: "ಧನ", - image: getAssetUrl(s3Assets.treasureAlpTelImage), - audio: getAssetAudioUrl(s3Assets.assetMoneyAndJewelsAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.assetMoneyAndJewelsAlpAudio), + image: getAssetUrl(s3Assets.ಧನImg), + audio: getAssetAudioUrl(s3Assets.ಧನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಧನSingleAudio), }, { - id: 55, + id: 62, title: "ವ್ಯಂಜನಗಳು", letters: "ಧ", letter: "ಧ", word: "ಬುಧವಾರ", - image: getAssetUrl(s3Assets.wednesdayAlpTelImage), - audio: getAssetAudioUrl(s3Assets.wednesdayAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.wednesdayAlpAudio), + image: getAssetUrl(s3Assets.ಬುಧವಾರImg), + audio: getAssetAudioUrl(s3Assets.ಬುಧವಾರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಬುಧವಾರSingleAudio), }, { - id: 56, + id: 63, title: "ವ್ಯಂಜನಗಳು", letters: "ಧ", letter: "ಧ", word: "ಔಷಧ", - image: getAssetUrl(s3Assets.medicineAlpTelImage), - audio: getAssetAudioUrl(s3Assets.medicineAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.medicineAlpAudio), + image: getAssetUrl(s3Assets.ಔಷಧ2Img), + audio: getAssetAudioUrl(s3Assets.ಔಷಧ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಔಷಧ2SingleAudio), }, ], }, @@ -1625,34 +1726,34 @@ const dataKn = [ letter: "ನ", items: [ { - id: 57, + id: 64, title: "ವ್ಯಂಜನಗಳು", letters: "ನ", letter: "ನ", word: "ನರಿ", - image: getAssetUrl(s3Assets.foxAlpTelImage), - audio: getAssetAudioUrl(s3Assets.foxAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.foxAlpAudio), + image: getAssetUrl(s3Assets.ನರಿImg), + audio: getAssetAudioUrl(s3Assets.ನರಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ನರಿSingleAudio), }, { - id: 58, + id: 65, title: "ವ್ಯಂಜನಗಳು", letters: "ನ", letter: "ನ", word: "ಕನಸು", - image: getAssetUrl(s3Assets.dreamAlpTelImage), - audio: getAssetAudioUrl(s3Assets.dreamAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.dreamAlpAudio), + image: getAssetUrl(s3Assets.ಕನಸುImg), + audio: getAssetAudioUrl(s3Assets.ಕನಸುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕನಸುSingleAudio), }, { - id: 59, + id: 66, title: "ವ್ಯಂಜನಗಳು", letters: "ನ", letter: "ನ", word: "ನಮನ", - image: getAssetUrl(s3Assets.pranamAlpTelImage), - audio: getAssetAudioUrl(s3Assets.namasteAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.namasteAlpAudio), + image: getAssetUrl(s3Assets.ನಮನImg), + audio: getAssetAudioUrl(s3Assets.ನಮನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ನಮನSingleAudio), }, ], }, @@ -1660,34 +1761,34 @@ const dataKn = [ letter: "ಪ", items: [ { - id: 60, + id: 67, title: "ವ್ಯಂಜನಗಳು", letters: "ಪ", letter: "ಪ", word: "ಪದಕ", - image: getAssetUrl(s3Assets.medalAlpTelImage), - audio: getAssetAudioUrl(s3Assets.medalAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.medalAlpAudio), + image: getAssetUrl(s3Assets.ಪದಕ2Img), + audio: getAssetAudioUrl(s3Assets.ಪದಕ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಪದಕ2SingleAudio), }, { - id: 61, + id: 68, title: "ವ್ಯಂಜನಗಳು", letters: "ಪ", letter: "ಪ", word: "ಗಾಳಿಪಟ", - image: getAssetUrl(s3Assets.kiteAlpTelImage), - audio: getAssetAudioUrl(s3Assets.kiteAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.kiteAlpAudio), + image: getAssetUrl(s3Assets.ಗಾಳಿಪಟImg), + audio: getAssetAudioUrl(s3Assets.ಗಾಳಿಪಟAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಗಾಳಿಪಟSingleAudio), }, { - id: 62, + id: 69, title: "ವ್ಯಂಜನಗಳು", letters: "ಪ", letter: "ಪ", word: "ಕೋಪ", - image: getAssetUrl(s3Assets.angerAlpTelImage), - audio: getAssetAudioUrl(s3Assets.angryBoyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.angryBoyAlpAudio), + image: getAssetUrl(s3Assets.ಕೋಪImg), + audio: getAssetAudioUrl(s3Assets.ಕೋಪAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕೋಪSingleAudio), }, ], }, @@ -1695,34 +1796,34 @@ const dataKn = [ letter: "ಫ", items: [ { - id: 63, + id: 70, title: "ವ್ಯಂಜನಗಳು", letters: "ಫ", letter: "ಫ", word: "ಫಲ", - image: getAssetUrl(s3Assets.fruitAlpTelImage), - audio: getAssetAudioUrl(s3Assets.fruitsAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.fruitsAlpAudio), + image: getAssetUrl(s3Assets.ಫಲImg), + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಫಲSingleAudio), }, { - id: 64, + id: 71, title: "ವ್ಯಂಜನಗಳು", letters: "ಫ", letter: "ಫ", word: "ಸೌರಫಲಕ", - image: getAssetUrl(s3Assets.solarAlpTelImage), - audio: getAssetAudioUrl(s3Assets.solarPanelAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.solarPanelAlpAudio), + image: getAssetUrl(s3Assets.ಸೌರಫಲಕImg), + audio: getAssetAudioUrl(s3Assets.ಸೌರಫಲಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸೌರಫಲಕSingleAudio), }, { - id: 65, + id: 72, title: "ವ್ಯಂಜನಗಳು", letters: "ಫ", letter: "ಫ", word: "ಕಫ", - image: getAssetUrl(s3Assets.coughAlpTelImage), - audio: getAssetAudioUrl(s3Assets.phlegmAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.phlegmAlpAudio), + image: getAssetUrl(s3Assets.ಕಫImg), + audio: getAssetAudioUrl(s3Assets.ಕಫAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಫSingleAudio), }, ], }, @@ -1730,34 +1831,34 @@ const dataKn = [ letter: "ಬ", items: [ { - id: 66, + id: 73, title: "ವ್ಯಂಜನಗಳು", letters: "ಬ", letter: "ಬ", word: "ಬಟಾಣಿ", - image: getAssetUrl(s3Assets.peasAlpTelImage), - audio: getAssetAudioUrl(s3Assets.greenPeasAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.greenPeasAlpAudio), + image: getAssetUrl(s3Assets.ಬಟಾಣಿImg), + audio: getAssetAudioUrl(s3Assets.ಬಟಾಣಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಬಟಾಣಿSingleAudio), }, { - id: 67, + id: 74, title: "ವ್ಯಂಜನಗಳು", letters: "ಬ", letter: "ಬ", word: "ತಬಲ", - image: getAssetUrl(s3Assets.tabalaAlpTelImage), - audio: getAssetAudioUrl(s3Assets.tabalaaAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.tabalaaAlpAudio), + image: getAssetUrl(s3Assets.ತಬಲ2Img), + audio: getAssetAudioUrl(s3Assets.ತಬಲ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ತಬಲ2SingleAudio), }, { - id: 68, + id: 75, title: "ವ್ಯಂಜನಗಳು", letters: "ಬ", letter: "ಬ", word: "ಕುಟುಂಬ", - image: getAssetUrl(s3Assets.familyAlpTelImage), - audio: getAssetAudioUrl(s3Assets.familyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.familyAlpAudio), + image: getAssetUrl(s3Assets.ಕುಟುಂಬImg), + audio: getAssetAudioUrl(s3Assets.ಕುಟುಂಬAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಕುಟುಂಬSingleAudio), }, ], }, @@ -1765,34 +1866,34 @@ const dataKn = [ letter: "ಭ", items: [ { - id: 69, + id: 76, title: "ವ್ಯಂಜನಗಳು", letters: "ಭ", letter: "ಭ", word: "ಭರಣಿ", - image: getAssetUrl(s3Assets.boxAlpTelImage), - audio: getAssetAudioUrl(s3Assets.smallBoxOrChestAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.smallBoxOrChestAlpAudio), + image: getAssetUrl(s3Assets.ಭರಣಿImg), + audio: getAssetAudioUrl(s3Assets.ಭರಣಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಭರಣಿSingleAudio), }, { - id: 70, + id: 77, title: "ವ್ಯಂಜನಗಳು", letters: "ಭ", letter: "ಭ", word: "ಆಭರಣ", - image: getAssetUrl(s3Assets.jewelleryAlpTelImage), - audio: getAssetAudioUrl(s3Assets.jewelariesAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.jewelariesAlpAudio), + image: getAssetUrl(s3Assets.ಆಭರಣImg), + audio: getAssetAudioUrl(s3Assets.ಆಭರಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಆಭರಣSingleAudio), }, { - id: 71, + id: 78, title: "ವ್ಯಂಜನಗಳು", letters: "ಭ", letter: "ಭ", word: "ವೃಷಭ", - image: getAssetUrl(s3Assets.bullAlpTelImage), - audio: getAssetAudioUrl(s3Assets.anOxOrBullAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.anOxOrBullAlpAudio), + image: getAssetUrl(s3Assets.ವೃಷಭImg), + audio: getAssetAudioUrl(s3Assets.ವೃಷಭAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವೃಷಭSingleAudio), }, ], }, @@ -1800,34 +1901,34 @@ const dataKn = [ letter: "ಮ", items: [ { - id: 72, + id: 79, title: "ವ್ಯಂಜನಗಳು", letters: "ಮ", letter: "ಮ", word: "ಮರ", - image: getAssetUrl(s3Assets.treeAlpTelImage), - audio: getAssetAudioUrl(s3Assets.treeAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.treeAlpAudio), + image: getAssetUrl(s3Assets.ಮರImg), + audio: getAssetAudioUrl(s3Assets.ಮರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಮರSingleAudio), }, { - id: 73, + id: 80, title: "ವ್ಯಂಜನಗಳು", letters: "ಮ", letter: "ಮ", word: "ಕಮಲ", - image: getAssetUrl(s3Assets.lotusAlpTelImage), - audio: getAssetAudioUrl(s3Assets.lotusAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.lotusAlpAudio), + image: getAssetUrl(s3Assets.ಕಮಲ2Img), + audio: getAssetAudioUrl(s3Assets.ಕಮಲ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಕಮಲ2SingleAudio), }, { - id: 74, + id: 81, title: "ವ್ಯಂಜನಗಳು", letters: "ಮ", letter: "ಮ", word: "ಹಿಮ", - image: getAssetUrl(s3Assets.snowAlpTelImage), - audio: getAssetAudioUrl(s3Assets.snowAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.snowAlpAudio), + image: getAssetUrl(s3Assets.ಹಿಮImg), + audio: getAssetAudioUrl(s3Assets.ಹಿಮAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಿಮSingleAudio), }, ], }, @@ -1835,34 +1936,34 @@ const dataKn = [ letter: "ಯ", items: [ { - id: 75, + id: 82, title: "ವ್ಯಂಜನಗಳು", letters: "ಯ", letter: "ಯ", word: "ಯಮ", - image: getAssetUrl(s3Assets.yamrajAlpTelImage), - audio: getAssetAudioUrl(s3Assets.yamaAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.yamaAlpAudio), + image: getAssetUrl(s3Assets.ಯಮImg), + audio: getAssetAudioUrl(s3Assets.ಯಮAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಯಮSingleAudio), }, { - id: 76, + id: 83, title: "ವ್ಯಂಜನಗಳು", letters: "ಯ", letter: "ಯ", word: "ಪಾಯಸ", - image: getAssetUrl(s3Assets.stewAlpTelImage), - audio: getAssetAudioUrl(s3Assets.kheerAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.kheerAlpAudio), + image: getAssetUrl(s3Assets.ಪಾಯಸImg), + audio: getAssetAudioUrl(s3Assets.ಪಾಯಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪಾಯಸSingleAudio), }, { - id: 77, + id: 84, title: "ವ್ಯಂಜನಗಳು", letters: "ಯ", letter: "ಯ", word: "ಭಯ", - image: getAssetUrl(s3Assets.fearAlpTelImage), - audio: getAssetAudioUrl(s3Assets.fearAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.fearAlpAudio), + image: getAssetUrl(s3Assets.ಭಯImg), + audio: getAssetAudioUrl(s3Assets.ಭಯAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಭಯSingleAudio), }, ], }, @@ -1870,34 +1971,34 @@ const dataKn = [ letter: "ರ", items: [ { - id: 78, + id: 85, title: "ವ್ಯಂಜನಗಳು", letters: "ರ", letter: "ರ", word: "ರಥ", - image: getAssetUrl(s3Assets.horsechariotAlpTelImage), - audio: getAssetAudioUrl(s3Assets.chariot1AlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.chariot1AlpAudio), + image: getAssetUrl(s3Assets.ರಥ2Img), + audio: getAssetAudioUrl(s3Assets.ರಥ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ರಥ2SingleAudio), }, { - id: 79, + id: 86, title: "ವ್ಯಂಜನಗಳು", letters: "ರ", letter: "ರ", word: "ಬೆರಳು", - image: getAssetUrl(s3Assets.fingerAlpTelImage), - audio: getAssetAudioUrl(s3Assets.fingerAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.fingerAlpAudio), + image: getAssetUrl(s3Assets.ಬೆರಳುImg), + audio: getAssetAudioUrl(s3Assets.ಬೆರಳುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಬೆರಳುSingleAudio), }, { - id: 80, + id: 87, title: "ವ್ಯಂಜನಗಳು", letters: "ರ", letter: "ರ", word: "ಉದರ", - image: getAssetUrl(s3Assets.bellyAlpTelImage), - audio: getAssetAudioUrl(s3Assets.bellyAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.bellyAlpAudio), + image: getAssetUrl(s3Assets.ಉದರ2Img), + audio: getAssetAudioUrl(s3Assets.ಉದರ2Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಉದರ2SingleAudio), }, ], }, @@ -1905,34 +2006,34 @@ const dataKn = [ letter: "ಲ", items: [ { - id: 81, + id: 88, title: "ವ್ಯಂಜನಗಳು", letters: "ಲ", letter: "ಲ", word: "ಲತೆ", - image: getAssetUrl(s3Assets.leafAlpTelImage), - audio: getAssetAudioUrl(s3Assets.creeperAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.creeperAlpAudio), + image: getAssetUrl(s3Assets.ಲತೆImg), + audio: getAssetAudioUrl(s3Assets.ಲತೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಲತೆSingleAudio), }, { - id: 82, + id: 89, title: "ವ್ಯಂಜನಗಳು", letters: "ಲ", letter: "ಲ", word: "ಚಿಲಕ", - image: getAssetUrl(s3Assets.colorpencilAlpTelImage), - audio: getAssetAudioUrl(s3Assets.boltAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.boltAlpAudio), + image: getAssetUrl(s3Assets.ಚಿಲಕImg), + audio: getAssetAudioUrl(s3Assets.ಚಿಲಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಚಿಲಕSingleAudio), }, { - id: 83, + id: 90, title: "ವ್ಯಂಜನಗಳು", letters: "ಲ", letter: "ಲ", word: "ಮೊಲ", - image: getAssetUrl(s3Assets.rabbitAlpTelImage), - audio: getAssetAudioUrl(s3Assets.rabbitAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.rabbitAlpAudio), + image: getAssetUrl(s3Assets.ಮೊಲImg), + audio: getAssetAudioUrl(s3Assets.ಮೊಲAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಮೊಲSingleAudio), }, ], }, @@ -1940,34 +2041,34 @@ const dataKn = [ letter: "ವ", items: [ { - id: 84, + id: 91, title: "ವ್ಯಂಜನಗಳು", letters: "ವ", letter: "ವ", word: "ವನ", - image: getAssetUrl(s3Assets.forestAlpTelImage), - audio: getAssetAudioUrl(s3Assets.jungleAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.jungleAlpAudio), + image: getAssetUrl(s3Assets.ವನImg), + audio: getAssetAudioUrl(s3Assets.ವನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವನSingleAudio), }, { - id: 85, + id: 92, title: "ವ್ಯಂಜನಗಳು", letters: "ವ", letter: "ವ", word: "ಲವಣ", - image: getAssetUrl(s3Assets.saltAlpTelImage), - audio: getAssetAudioUrl(s3Assets.rockSaltAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.rockSaltAlpAudio), + image: getAssetUrl(s3Assets.ಲವಣImg), + audio: getAssetAudioUrl(s3Assets.ಲವಣAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಲವಣSingleAudio), }, { - id: 86, + id: 93, title: "ವ್ಯಂಜನಗಳು", letters: "ವ", letter: "ವ", word: "ಶಿವ", - image: getAssetUrl(s3Assets.shivAlpTelImage), - audio: getAssetAudioUrl(s3Assets.shivaGodAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.shivaGodAlpAudio), + image: getAssetUrl(s3Assets.ಶಿವImg), + audio: getAssetAudioUrl(s3Assets.ಶಿವAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಶಿವSingleAudio), }, ], }, @@ -1975,34 +2076,34 @@ const dataKn = [ letter: "ಶ", items: [ { - id: 87, + id: 94, title: "ವ್ಯಂಜನಗಳು", letters: "ಶ", letter: "ಶ", word: "ಶಶಿ", - image: getAssetUrl(s3Assets.moonAlpTelImage), - audio: getAssetAudioUrl(s3Assets.moonAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.moonAlpAudio), + image: getAssetUrl(s3Assets.ಶಶಿImg), + audio: getAssetAudioUrl(s3Assets.ಶಶಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಶಶಿSingleAudio), }, { - id: 88, + id: 95, title: "ವ್ಯಂಜನಗಳು", letters: "ಶ", letter: "ಶ", word: "ದಶಕ", - image: getAssetUrl(s3Assets.fiveAlpTelImage), - audio: getAssetAudioUrl(s3Assets.tenInASetAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.tenInASetAlpAudio), + image: getAssetUrl(s3Assets.ದಶಕImg), + audio: getAssetAudioUrl(s3Assets.ದಶಕAudio), + singleAudio: getAssetAudioUrl(s3Assets.ದಶಕSingleAudio), }, { - id: 89, + id: 96, title: "ವ್ಯಂಜನಗಳು", letters: "ಶ", letter: "ಶ", word: "ದೇಶ", - image: getAssetUrl(s3Assets.countryAlpTelImage), - audio: getAssetAudioUrl(s3Assets.countryAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.countryAlpAudio), + image: getAssetUrl(s3Assets.ದೇಶImg), + audio: getAssetAudioUrl(s3Assets.ದೇಶAudio), + singleAudio: getAssetAudioUrl(s3Assets.ದೇಶSingleAudio), }, ], }, @@ -2010,34 +2111,34 @@ const dataKn = [ letter: "ಷ", items: [ { - id: 90, + id: 97, title: "ವ್ಯಂಜನಗಳು", letters: "ಷ", letter: "ಷ", word: "ಷಡ್ಭುಜ", - image: getAssetUrl(s3Assets.hexagonAlpTelImage), - audio: getAssetAudioUrl(s3Assets.hexagoneAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.hexagoneAlpAudio), + image: getAssetUrl(s3Assets.ಷಡ್ಭುಜImg), + audio: getAssetAudioUrl(s3Assets.ಷಡ್ಭುಜAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಷಡ್ಭುಜSingleAudio), }, { - id: 91, + id: 98, title: "ವ್ಯಂಜನಗಳು", letters: "ಷ", letter: "ಷ", word: "ಔಷಧ", - image: getAssetUrl(s3Assets.medicineAlpTelImage), - audio: getAssetAudioUrl(s3Assets.medicine2AlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.medicine2AlpAudio), + image: getAssetUrl(s3Assets.ಔಷಧ3Img), + audio: getAssetAudioUrl(s3Assets.ಔಷಧ3Audio), + singleAudio: getAssetAudioUrl(s3Assets.ಔಷಧ3SingleAudio), }, { - id: 92, + id: 99, title: "ವ್ಯಂಜನಗಳು", letters: "ಷ", letter: "ಷ", word: "ಪುರುಷ", - image: getAssetUrl(s3Assets.manAlpTelImage), - audio: getAssetAudioUrl(s3Assets.purushaManAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.purushaManAlpAudio), + image: getAssetUrl(s3Assets.ಪುರುಷImg), + audio: getAssetAudioUrl(s3Assets.ಪುರುಷAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಪುರುಷSingleAudio), }, ], }, @@ -2045,34 +2146,34 @@ const dataKn = [ letter: "ಸ", items: [ { - id: 93, + id: 100, title: "ವ್ಯಂಜನಗಳು", letters: "ಸ", letter: "ಸ", word: "ಸರ", - image: getAssetUrl(s3Assets.tinklechainAlpTelImage), - audio: getAssetAudioUrl(s3Assets.goldChainAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.goldChainAlpAudio), + image: getAssetUrl(s3Assets.ಸರImg), + audio: getAssetAudioUrl(s3Assets.ಸರAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸರSingleAudio), }, { - id: 94, + id: 101, title: "ವ್ಯಂಜನಗಳು", letters: "ಸ", letter: "ಸ", word: "ಮೊಸಳೆ", - image: getAssetUrl(s3Assets.crocodileAlpTelImage), - audio: getAssetAudioUrl(s3Assets.crocodileAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.crocodileAlpAudio), + image: getAssetUrl(s3Assets.ಮೊಸಳೆImg), + audio: getAssetAudioUrl(s3Assets.ಮೊಸಳೆAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಮೊಸಳೆSingleAudio), }, { - id: 95, + id: 102, title: "ವ್ಯಂಜನಗಳು", letters: "ಸ", letter: "ಸ", word: "ಹಂಸ", - image: getAssetUrl(s3Assets.swanAlpTelImage), - audio: getAssetAudioUrl(s3Assets.swanAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.swanAlpAudio), + image: getAssetUrl(s3Assets.ಹಂಸImg), + audio: getAssetAudioUrl(s3Assets.ಹಂಸAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಂಸSingleAudio), }, ], }, @@ -2080,34 +2181,34 @@ const dataKn = [ letter: "ಹ", items: [ { - id: 96, + id: 103, title: "ವ್ಯಂಜನಗಳು", letters: "ಹ", letter: "ಹ", word: "ಹಸು", - image: getAssetUrl(s3Assets.cowAlpTelImage), - audio: getAssetAudioUrl(s3Assets.cow2AlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.cow2AlpAudio), + image: getAssetUrl(s3Assets.ಹಸುImg), + audio: getAssetAudioUrl(s3Assets.ಹಸುAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಸುSingleAudio), }, { - id: 97, + id: 104, title: "ವ್ಯಂಜನಗಳು", letters: "ಹ", letter: "ಹ", word: "ವಾಹನ", - image: getAssetUrl(s3Assets.carAlpTelImage), - audio: getAssetAudioUrl(s3Assets.vehicleAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.vehicleAlpAudio), + image: getAssetUrl(s3Assets.ವಾಹನImg), + audio: getAssetAudioUrl(s3Assets.ವಾಹನAudio), + singleAudio: getAssetAudioUrl(s3Assets.ವಾಹನSingleAudio), }, { - id: 98, + id: 105, title: "ವ್ಯಂಜನಗಳು", letters: "ಹ", letter: "ಹ", word: "ಸಿಂಹ", - image: getAssetUrl(s3Assets.lionAlpTelImage), - audio: getAssetAudioUrl(s3Assets.lionAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.lionAlpAudio), + image: getAssetUrl(s3Assets.ಸಿಂಹImg), + audio: getAssetAudioUrl(s3Assets.ಸಿಂಹAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಸಿಂಹSingleAudio), }, ], }, @@ -2115,14 +2216,14 @@ const dataKn = [ letter: "ಳ", items: [ { - id: 99, + id: 106, title: "ವ್ಯಂಜನಗಳು", letters: "ಳ", letter: "ಳ", word: "ಹಳದಿ", - image: getAssetUrl(s3Assets.yellowAlpTelImage), - audio: getAssetAudioUrl(s3Assets.yellowAlpAudio), - singleAudio: getAssetAudioUrl(s3Assets.yellowAlpAudio), + image: getAssetUrl(s3Assets.ಹಳದಿImg), + audio: getAssetAudioUrl(s3Assets.ಹಳದಿAudio), + singleAudio: getAssetAudioUrl(s3Assets.ಹಳದಿSingleAudio), }, ], }, @@ -3422,7 +3523,7 @@ const dataTe = [ word: "అల", image: getAssetUrl(s3Assets.అలImg), audio: getAssetAudioUrl(s3Assets.అలAudio), - singleAudio: getAssetAudioUrl(s3Assets.అలAudio), + singleAudio: getAssetAudioUrl(s3Assets.అలSingleAudio), }, ], }, @@ -3437,7 +3538,7 @@ const dataTe = [ word: "ఆట", image: getAssetUrl(s3Assets.ఆటImg), audio: getAssetAudioUrl(s3Assets.ఆటAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఆటAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఆటSingleAudio), }, ], }, @@ -3452,7 +3553,7 @@ const dataTe = [ word: "ఇల", image: getAssetUrl(s3Assets.ఇలImg), audio: getAssetAudioUrl(s3Assets.ఇలAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఇలAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఇలSingleAudio), }, ], }, @@ -3467,7 +3568,7 @@ const dataTe = [ word: "ఈగ", image: getAssetUrl(s3Assets.ఈగImg), audio: getAssetAudioUrl(s3Assets.ఈగAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఈగAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఈగSingleAudio), }, ], }, @@ -3482,7 +3583,7 @@ const dataTe = [ word: "ఉడుత", image: getAssetUrl(s3Assets.ఉడుతImg), audio: getAssetAudioUrl(s3Assets.ఉడుతAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఉడుతAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉడుతSingleAudio), }, ], }, @@ -3497,7 +3598,7 @@ const dataTe = [ word: "ఊయల", image: getAssetUrl(s3Assets.ఊయలImg), audio: getAssetAudioUrl(s3Assets.ఊయలAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఊయలAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఊయలSingleAudio), }, ], }, @@ -3512,7 +3613,7 @@ const dataTe = [ word: "ఋషి", image: getAssetUrl(s3Assets.ఋషిImg), audio: getAssetAudioUrl(s3Assets.ఋషిAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఋషిAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఋషిSingleAudio), }, ], }, @@ -3527,7 +3628,7 @@ const dataTe = [ word: "ౠక", image: getAssetUrl(s3Assets.ౠకImg), audio: getAssetAudioUrl(s3Assets.ౠకAudio), - singleAudio: getAssetAudioUrl(s3Assets.ౠకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ౠకSingleAudio), }, ], }, @@ -3542,7 +3643,7 @@ const dataTe = [ word: "ఎలుక", image: getAssetUrl(s3Assets.ఎలుకImg), audio: getAssetAudioUrl(s3Assets.ఎలుకAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఎలుకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఎలుకSingleAudio), }, ], }, @@ -3557,7 +3658,7 @@ const dataTe = [ word: "ఏనుగు", image: getAssetUrl(s3Assets.ఏనుగుImg), audio: getAssetAudioUrl(s3Assets.ఏనుగుAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఏనుగుAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఏనుగుSingleAudio), }, ], }, @@ -3572,7 +3673,7 @@ const dataTe = [ word: "ఐదు", image: getAssetUrl(s3Assets.ఐదుImg), audio: getAssetAudioUrl(s3Assets.ఐదుAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఐదుAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఐదుSingleAudio), }, ], }, @@ -3587,7 +3688,7 @@ const dataTe = [ word: "ఒక", image: getAssetUrl(s3Assets.ఒకImg), audio: getAssetAudioUrl(s3Assets.ఒకAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఒకAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఒకSingleAudio), }, ], }, @@ -3602,7 +3703,7 @@ const dataTe = [ word: "ఓడ", image: getAssetUrl(s3Assets.ఓడImg), audio: getAssetAudioUrl(s3Assets.ఓడAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఓడAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఓడSingleAudio), }, ], }, @@ -3617,7 +3718,7 @@ const dataTe = [ word: "ఔషధం", image: getAssetUrl(s3Assets.ఔషధంImg), audio: getAssetAudioUrl(s3Assets.ఔషధంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఔషధంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఔషధంSingleAudio), }, ], }, @@ -3632,42 +3733,57 @@ const dataTe = [ word: "అంగడి", image: getAssetUrl(s3Assets.అంగడిImg), audio: getAssetAudioUrl(s3Assets.అంగడిAudio), - singleAudio: getAssetAudioUrl(s3Assets.అంగడిAudio), + singleAudio: getAssetAudioUrl(s3Assets.అంగడిSingleAudio), }, ], }, { - letter: "క", + letter: "అః", items: [ { id: 16, + title: "అచ్చులు", + letters: "అః", + letter: "అః", + word: "అః", + image: getAssetUrl(s3Assets.అఃImg), + audio: getAssetAudioUrl(s3Assets.అఃAudio), + singleAudio: getAssetAudioUrl(s3Assets.అఃSingleAudio), + }, + ], + }, + { + letter: "క", + items: [ + { + id: 17, title: "హల్లులు", letters: "క", letter: "క", word: "కల", image: getAssetUrl(s3Assets.కలImg), audio: getAssetAudioUrl(s3Assets.కలAudio), - singleAudio: getAssetAudioUrl(s3Assets.కలAudio), + singleAudio: getAssetAudioUrl(s3Assets.కలSingleAudio), }, { - id: 17, + id: 18, title: "హల్లులు", letters: "క", letter: "క", word: "ఆకలి", image: getAssetUrl(s3Assets.ఆకలిImg), audio: getAssetAudioUrl(s3Assets.ఆకలిAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఆకలిAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఆకలిSingleAudio), }, { - id: 18, + id: 19, title: "హల్లులు", letters: "క", letter: "క", word: "చిలుక", image: getAssetUrl(s3Assets.చిలుకImg), audio: getAssetAudioUrl(s3Assets.చిలుకAudio), - singleAudio: getAssetAudioUrl(s3Assets.చిలుకAudio), + singleAudio: getAssetAudioUrl(s3Assets.చిలుకSingleAudio), }, ], }, @@ -3675,14 +3791,14 @@ const dataTe = [ letter: "ఖ", items: [ { - id: 19, + id: 20, title: "హల్లులు", letters: "ఖ", letter: "ఖ", word: "ఖరం", image: getAssetUrl(s3Assets.ఖరంImg), audio: getAssetAudioUrl(s3Assets.ఖరంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఖరంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఖరంSingleAudio), }, ], }, @@ -3690,34 +3806,34 @@ const dataTe = [ letter: "గ", items: [ { - id: 20, + id: 21, title: "హల్లులు", letters: "గ", letter: "గ", word: "గద", image: getAssetUrl(s3Assets.గదImg), audio: getAssetAudioUrl(s3Assets.గదAudio), - singleAudio: getAssetAudioUrl(s3Assets.గదAudio), + singleAudio: getAssetAudioUrl(s3Assets.గదSingleAudio), }, { - id: 21, + id: 22, title: "హల్లులు", letters: "గ", letter: "గ", word: "ఉంగరం", image: getAssetUrl(s3Assets.ఉంగరంImg), audio: getAssetAudioUrl(s3Assets.ఉంగరంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఉంగరంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉంగరంSingleAudio), }, { - id: 22, + id: 23, title: "హల్లులు", letters: "గ", letter: "గ", word: "పండుగ", image: getAssetUrl(s3Assets.పండుగImg), audio: getAssetAudioUrl(s3Assets.పండుగAudio), - singleAudio: getAssetAudioUrl(s3Assets.పండుగAudio), + singleAudio: getAssetAudioUrl(s3Assets.పండుగSingleAudio), }, ], }, @@ -3725,24 +3841,39 @@ const dataTe = [ letter: "ఘ", items: [ { - id: 23, + id: 24, title: "హల్లులు", letters: "ఘ", letter: "ఘ", word: "ఘటం", image: getAssetUrl(s3Assets.ఘటంImg), audio: getAssetAudioUrl(s3Assets.ఘటంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఘటంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఘటంSingleAudio), }, { - id: 24, + id: 25, title: "హల్లులు", letters: "ఘ", letter: "ఘ", word: "మేఘం", image: getAssetUrl(s3Assets.మేఘంImg), audio: getAssetAudioUrl(s3Assets.మేఘంAudio), - singleAudio: getAssetAudioUrl(s3Assets.మేఘంAudio), + singleAudio: getAssetAudioUrl(s3Assets.మేఘంSingleAudio), + }, + ], + }, + { + letter: "ఙ", + items: [ + { + id: 26, + title: "హల్లులు", + letters: "ఙ", + letter: "ఙ", + word: "ఙ", + image: getAssetUrl(s3Assets.ఙImg), + audio: getAssetAudioUrl(s3Assets.ఙAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఙSingleAudio), }, ], }, @@ -3750,34 +3881,34 @@ const dataTe = [ letter: "చ", items: [ { - id: 25, + id: 27, title: "హల్లులు", letters: "చ", letter: "చ", word: "చరకా", image: getAssetUrl(s3Assets.చరకాImg), audio: getAssetAudioUrl(s3Assets.చరకాAudio), - singleAudio: getAssetAudioUrl(s3Assets.చరకాAudio), + singleAudio: getAssetAudioUrl(s3Assets.చరకాSingleAudio), }, { - id: 26, + id: 28, title: "హల్లులు", letters: "చ", letter: "చ", word: "రచన", image: getAssetUrl(s3Assets.రచనImg), audio: getAssetAudioUrl(s3Assets.రచనAudio), - singleAudio: getAssetAudioUrl(s3Assets.రచనAudio), + singleAudio: getAssetAudioUrl(s3Assets.రచనSingleAudio), }, { - id: 27, + id: 29, title: "హల్లులు", letters: "చ", letter: "చ", word: "కిచకిచ", image: getAssetUrl(s3Assets.కిచకిచImg), audio: getAssetAudioUrl(s3Assets.కిచకిచAudio), - singleAudio: getAssetAudioUrl(s3Assets.కిచకిచAudio), + singleAudio: getAssetAudioUrl(s3Assets.కిచకిచSingleAudio), }, ], }, @@ -3785,14 +3916,14 @@ const dataTe = [ letter: "ఛ", items: [ { - id: 28, + id: 30, title: "హల్లులు", letters: "ఛ", letter: "ఛ", word: "ఛత్రము", image: getAssetUrl(s3Assets.ఛత్రముImg), audio: getAssetAudioUrl(s3Assets.ఛత్రముAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఛత్రముAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఛత్రముSingleAudio), }, ], }, @@ -3800,34 +3931,34 @@ const dataTe = [ letter: "జ", items: [ { - id: 29, + id: 31, title: "హల్లులు", letters: "జ", letter: "జ", word: "జడ", image: getAssetUrl(s3Assets.జడImg), audio: getAssetAudioUrl(s3Assets.జడAudio), - singleAudio: getAssetAudioUrl(s3Assets.జడAudio), + singleAudio: getAssetAudioUrl(s3Assets.జడSingleAudio), }, { - id: 30, + id: 32, title: "హల్లులు", letters: "జ", letter: "జ", word: "కంజర", image: getAssetUrl(s3Assets.కంజరImg), audio: getAssetAudioUrl(s3Assets.కంజరAudio), - singleAudio: getAssetAudioUrl(s3Assets.కంజరAudio), + singleAudio: getAssetAudioUrl(s3Assets.కంజరSingleAudio), }, { - id: 31, + id: 33, title: "హల్లులు", letters: "జ", letter: "జ", word: "జలజ", image: getAssetUrl(s3Assets.జలజImg), audio: getAssetAudioUrl(s3Assets.జలజAudio), - singleAudio: getAssetAudioUrl(s3Assets.జలజAudio), + singleAudio: getAssetAudioUrl(s3Assets.జలజSingleAudio), }, ], }, @@ -3835,14 +3966,29 @@ const dataTe = [ letter: "ఝ", items: [ { - id: 32, + id: 34, title: "హల్లులు", letters: "ఝ", letter: "ఝ", word: "ఝషం", image: getAssetUrl(s3Assets.ఝషంImg), audio: getAssetAudioUrl(s3Assets.ఝషంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఝషంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఝషంSingleAudio), + }, + ], + }, + { + letter: "ఞ", + items: [ + { + id: 35, + title: "హల్లులు", + letters: "ఞ", + letter: "ఞ", + word: "ఞ", + image: getAssetUrl(s3Assets.ఞImg), + audio: getAssetAudioUrl(s3Assets.ఞAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఞSingleAudio), }, ], }, @@ -3850,34 +3996,34 @@ const dataTe = [ letter: "ట", items: [ { - id: 33, + id: 36, title: "హల్లులు", letters: "ట", letter: "ట", word: "టమాట", image: getAssetUrl(s3Assets.టమాటImg), audio: getAssetAudioUrl(s3Assets.టమాటAudio), - singleAudio: getAssetAudioUrl(s3Assets.టమాటAudio), + singleAudio: getAssetAudioUrl(s3Assets.టమాటSingleAudio), }, { - id: 34, + id: 37, title: "హల్లులు", letters: "ట", letter: "ట", word: "నాటకం", image: getAssetUrl(s3Assets.నాటకంImg), audio: getAssetAudioUrl(s3Assets.నాటకంAudio), - singleAudio: getAssetAudioUrl(s3Assets.నాటకంAudio), + singleAudio: getAssetAudioUrl(s3Assets.నాటకంSingleAudio), }, { - id: 35, + id: 38, title: "హల్లులు", letters: "ట", letter: "ట", word: "తోట", image: getAssetUrl(s3Assets.తోటImg), audio: getAssetAudioUrl(s3Assets.తోటAudio), - singleAudio: getAssetAudioUrl(s3Assets.తోటAudio), + singleAudio: getAssetAudioUrl(s3Assets.తోటSingleAudio), }, ], }, @@ -3885,24 +4031,24 @@ const dataTe = [ letter: "ఠ", items: [ { - id: 36, + id: 39, title: "హల్లులు", letters: "ఠ", letter: "ఠ", word: "పాఠశాల", image: getAssetUrl(s3Assets.పాఠశాలImg), audio: getAssetAudioUrl(s3Assets.పాఠశాలAudio), - singleAudio: getAssetAudioUrl(s3Assets.పాఠశాలAudio), + singleAudio: getAssetAudioUrl(s3Assets.పాఠశాలSingleAudio), }, { - id: 37, + id: 40, title: "హల్లులు", letters: "ఠ", letter: "ఠ", word: "పాఠం", image: getAssetUrl(s3Assets.పాఠంImg), audio: getAssetAudioUrl(s3Assets.పాఠంAudio), - singleAudio: getAssetAudioUrl(s3Assets.పాఠంAudio), + singleAudio: getAssetAudioUrl(s3Assets.పాఠంSingleAudio), }, ], }, @@ -3910,34 +4056,34 @@ const dataTe = [ letter: "డ", items: [ { - id: 38, + id: 41, title: "హల్లులు", letters: "డ", letter: "డ", word: "డబ్బా", image: getAssetUrl(s3Assets.డబ్బాImg), audio: getAssetAudioUrl(s3Assets.డబ్బాAudio), - singleAudio: getAssetAudioUrl(s3Assets.డబ్బాAudio), + singleAudio: getAssetAudioUrl(s3Assets.డబ్బాSingleAudio), }, { - id: 39, + id: 42, title: "హల్లులు", letters: "డ", letter: "డ", word: "అడవి", image: getAssetUrl(s3Assets.అడవిImg), audio: getAssetAudioUrl(s3Assets.అడవిAudio), - singleAudio: getAssetAudioUrl(s3Assets.అడవిAudio), + singleAudio: getAssetAudioUrl(s3Assets.అడవిSingleAudio), }, { - id: 40, + id: 43, title: "హల్లులు", letters: "డ", letter: "డ", word: "బండ", image: getAssetUrl(s3Assets.బండImg), audio: getAssetAudioUrl(s3Assets.బండAudio), - singleAudio: getAssetAudioUrl(s3Assets.బండAudio), + singleAudio: getAssetAudioUrl(s3Assets.బండSingleAudio), }, ], }, @@ -3945,14 +4091,14 @@ const dataTe = [ letter: "ఢ", items: [ { - id: 41, + id: 44, title: "హల్లులు", letters: "ఢ", letter: "ఢ", word: "ఢమఢమ", image: getAssetUrl(s3Assets.ఢమఢమImg), audio: getAssetAudioUrl(s3Assets.ఢమఢమAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఢమఢమAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఢమఢమSingleAudio), }, ], }, @@ -3960,24 +4106,24 @@ const dataTe = [ letter: "ణ", items: [ { - id: 42, + id: 45, title: "హల్లులు", letters: "ణ", letter: "ణ", word: "గణపతి", image: getAssetUrl(s3Assets.గణపతిImg), audio: getAssetAudioUrl(s3Assets.గణపతిAudio), - singleAudio: getAssetAudioUrl(s3Assets.గణపతిAudio), + singleAudio: getAssetAudioUrl(s3Assets.గణపతిSingleAudio), }, { - id: 43, + id: 46, title: "హల్లులు", letters: "ణ", letter: "ణ", word: "వీణ", image: getAssetUrl(s3Assets.వీణImg), audio: getAssetAudioUrl(s3Assets.వీణAudio), - singleAudio: getAssetAudioUrl(s3Assets.వీణAudio), + singleAudio: getAssetAudioUrl(s3Assets.వీణSingleAudio), }, ], }, @@ -3985,34 +4131,34 @@ const dataTe = [ letter: "త", items: [ { - id: 44, + id: 47, title: "హల్లులు", letters: "త", letter: "త", word: "తల", image: getAssetUrl(s3Assets.తలImg), audio: getAssetAudioUrl(s3Assets.తలAudio), - singleAudio: getAssetAudioUrl(s3Assets.తలAudio), + singleAudio: getAssetAudioUrl(s3Assets.తలSingleAudio), }, { - id: 45, + id: 48, title: "హల్లులు", letters: "త", letter: "త", word: "జాతర", image: getAssetUrl(s3Assets.జాతరImg), audio: getAssetAudioUrl(s3Assets.జాతరAudio), - singleAudio: getAssetAudioUrl(s3Assets.జాతరAudio), + singleAudio: getAssetAudioUrl(s3Assets.జాతరSingleAudio), }, { - id: 46, + id: 49, title: "హల్లులు", letters: "త", letter: "త", word: "ఈత", image: getAssetUrl(s3Assets.ఈతImg), audio: getAssetAudioUrl(s3Assets.ఈతAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఈతAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఈతSingleAudio), }, ], }, @@ -4020,34 +4166,34 @@ const dataTe = [ letter: "థ", items: [ { - id: 47, + id: 50, title: "హల్లులు", letters: "థ", letter: "థ", word: "థర్మోస్", image: getAssetUrl(s3Assets.థర్మోస్Img), audio: getAssetAudioUrl(s3Assets.థర్మోస్Audio), - singleAudio: getAssetAudioUrl(s3Assets.థర్మోస్Audio), + singleAudio: getAssetAudioUrl(s3Assets.థర్మోస్SingleAudio), }, { - id: 48, + id: 51, title: "హల్లులు", letters: "థ", letter: "థ", word: "రథము", image: getAssetUrl(s3Assets.రథముImg), audio: getAssetAudioUrl(s3Assets.రథముAudio), - singleAudio: getAssetAudioUrl(s3Assets.రథముAudio), + singleAudio: getAssetAudioUrl(s3Assets.రథముSingleAudio), }, { - id: 49, + id: 52, title: "హల్లులు", letters: "థ", letter: "థ", word: "కథ", image: getAssetUrl(s3Assets.కథImg), audio: getAssetAudioUrl(s3Assets.కథAudio), - singleAudio: getAssetAudioUrl(s3Assets.కథAudio), + singleAudio: getAssetAudioUrl(s3Assets.కథSingleAudio), }, ], }, @@ -4055,34 +4201,34 @@ const dataTe = [ letter: "ద", items: [ { - id: 50, + id: 53, title: "హల్లులు", letters: "ద", letter: "ద", word: "దవడ", image: getAssetUrl(s3Assets.దవడImg), audio: getAssetAudioUrl(s3Assets.దవడAudio), - singleAudio: getAssetAudioUrl(s3Assets.దవడAudio), + singleAudio: getAssetAudioUrl(s3Assets.దవడSingleAudio), }, { - id: 51, + id: 54, title: "హల్లులు", letters: "ద", letter: "ద", word: "ఉదయం", image: getAssetUrl(s3Assets.ఉదయంImg), audio: getAssetAudioUrl(s3Assets.ఉదయంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఉదయంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉదయంSingleAudio), }, { - id: 52, + id: 55, title: "హల్లులు", letters: "ద", letter: "ద", word: "కింద", image: getAssetUrl(s3Assets.కిందImg), audio: getAssetAudioUrl(s3Assets.కిందAudio), - singleAudio: getAssetAudioUrl(s3Assets.కిందAudio), + singleAudio: getAssetAudioUrl(s3Assets.కిందSingleAudio), }, ], }, @@ -4090,24 +4236,24 @@ const dataTe = [ letter: "ధ", items: [ { - id: 53, + id: 56, title: "హల్లులు", letters: "ధ", letter: "ధ", word: "ధనం", image: getAssetUrl(s3Assets.ధనంImg), audio: getAssetAudioUrl(s3Assets.ధనంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ధనంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ధనంSingleAudio), }, { - id: 54, + id: 57, title: "హల్లులు", letters: "ధ", letter: "ధ", word: "బాధ", image: getAssetUrl(s3Assets.బాధImg), audio: getAssetAudioUrl(s3Assets.బాధAudio), - singleAudio: getAssetAudioUrl(s3Assets.బాధAudio), + singleAudio: getAssetAudioUrl(s3Assets.బాధSingleAudio), }, ], }, @@ -4115,34 +4261,34 @@ const dataTe = [ letter: "న", items: [ { - id: 55, + id: 58, title: "హల్లులు", letters: "న", letter: "న", word: "నగ", image: getAssetUrl(s3Assets.నగImg), audio: getAssetAudioUrl(s3Assets.నగAudio), - singleAudio: getAssetAudioUrl(s3Assets.నగAudio), + singleAudio: getAssetAudioUrl(s3Assets.నగSingleAudio), }, { - id: 56, + id: 59, title: "హల్లులు", letters: "న", letter: "న", word: "అనప", image: getAssetUrl(s3Assets.అనపImg), audio: getAssetAudioUrl(s3Assets.అనపAudio), - singleAudio: getAssetAudioUrl(s3Assets.అనపAudio), + singleAudio: getAssetAudioUrl(s3Assets.అనపSingleAudio), }, { - id: 57, + id: 60, title: "హల్లులు", letters: "న", letter: "న", word: "వాన", image: getAssetUrl(s3Assets.వానImg), audio: getAssetAudioUrl(s3Assets.వానAudio), - singleAudio: getAssetAudioUrl(s3Assets.వానAudio), + singleAudio: getAssetAudioUrl(s3Assets.వానSingleAudio), }, ], }, @@ -4150,34 +4296,34 @@ const dataTe = [ letter: "ప", items: [ { - id: 58, + id: 61, title: "హల్లులు", letters: "ప", letter: "ప", word: "పలక", image: getAssetUrl(s3Assets.పలకImg), audio: getAssetAudioUrl(s3Assets.పలకAudio), - singleAudio: getAssetAudioUrl(s3Assets.పలకAudio), + singleAudio: getAssetAudioUrl(s3Assets.పలకSingleAudio), }, { - id: 59, + id: 62, title: "హల్లులు", letters: "ప", letter: "ప", word: "చేపలు", image: getAssetUrl(s3Assets.చేపలుImg), audio: getAssetAudioUrl(s3Assets.చేపలుAudio), - singleAudio: getAssetAudioUrl(s3Assets.చేపలుAudio), + singleAudio: getAssetAudioUrl(s3Assets.చేపలుSingleAudio), }, { - id: 60, + id: 63, title: "హల్లులు", letters: "ప", letter: "ప", word: "పాప", image: getAssetUrl(s3Assets.పాపImg), audio: getAssetAudioUrl(s3Assets.పాపAudio), - singleAudio: getAssetAudioUrl(s3Assets.పాపAudio), + singleAudio: getAssetAudioUrl(s3Assets.పాపSingleAudio), }, ], }, @@ -4185,14 +4331,14 @@ const dataTe = [ letter: "ఫ", items: [ { - id: 61, + id: 64, title: "హల్లులు", letters: "ఫ", letter: "ఫ", word: "ఫలము", image: getAssetUrl(s3Assets.ఫలముImg), audio: getAssetAudioUrl(s3Assets.ఫలముAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఫలముAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఫలముSingleAudio), }, ], }, @@ -4200,24 +4346,24 @@ const dataTe = [ letter: "బ", items: [ { - id: 62, + id: 65, title: "హల్లులు", letters: "బ", letter: "బ", word: "బంతి", image: getAssetUrl(s3Assets.బంతిImg), audio: getAssetAudioUrl(s3Assets.బంతిAudio), - singleAudio: getAssetAudioUrl(s3Assets.బంతిAudio), + singleAudio: getAssetAudioUrl(s3Assets.బంతిSingleAudio), }, { - id: 63, + id: 66, title: "హల్లులు", letters: "బ", letter: "బ", word: "తబల", image: getAssetUrl(s3Assets.తబలImg), audio: getAssetAudioUrl(s3Assets.తబలAudio), - singleAudio: getAssetAudioUrl(s3Assets.తబలAudio), + singleAudio: getAssetAudioUrl(s3Assets.తబలSingleAudio), }, ], }, @@ -4225,24 +4371,24 @@ const dataTe = [ letter: "భ", items: [ { - id: 64, + id: 67, title: "హల్లులు", letters: "భ", letter: "భ", word: "భవనం", image: getAssetUrl(s3Assets.భవనంImg), audio: getAssetAudioUrl(s3Assets.భవనంAudio), - singleAudio: getAssetAudioUrl(s3Assets.భవనంAudio), + singleAudio: getAssetAudioUrl(s3Assets.భవనంSingleAudio), }, { - id: 65, + id: 68, title: "హల్లులు", letters: "భ", letter: "భ", word: "సభ", image: getAssetUrl(s3Assets.సభImg), audio: getAssetAudioUrl(s3Assets.సభAudio), - singleAudio: getAssetAudioUrl(s3Assets.సభAudio), + singleAudio: getAssetAudioUrl(s3Assets.సభSingleAudio), }, ], }, @@ -4250,34 +4396,34 @@ const dataTe = [ letter: "మ", items: [ { - id: 66, + id: 69, title: "హల్లులు", letters: "మ", letter: "మ", word: "మర", image: getAssetUrl(s3Assets.మరImg), audio: getAssetAudioUrl(s3Assets.మరAudio), - singleAudio: getAssetAudioUrl(s3Assets.మరAudio), + singleAudio: getAssetAudioUrl(s3Assets.మరSingleAudio), }, { - id: 67, + id: 70, title: "హల్లులు", letters: "మ", letter: "మ", word: "నెమలి", image: getAssetUrl(s3Assets.నెమలిImg), audio: getAssetAudioUrl(s3Assets.నెమలిAudio), - singleAudio: getAssetAudioUrl(s3Assets.నెమలిAudio), + singleAudio: getAssetAudioUrl(s3Assets.నెమలిSingleAudio), }, { - id: 68, + id: 71, title: "హల్లులు", letters: "మ", letter: "మ", word: "చీమ", image: getAssetUrl(s3Assets.చీమImg), audio: getAssetAudioUrl(s3Assets.చీమAudio), - singleAudio: getAssetAudioUrl(s3Assets.చీమAudio), + singleAudio: getAssetAudioUrl(s3Assets.చీమSingleAudio), }, ], }, @@ -4285,34 +4431,34 @@ const dataTe = [ letter: "య", items: [ { - id: 69, + id: 72, title: "హల్లులు", letters: "య", letter: "య", word: "యద", image: getAssetUrl(s3Assets.యదImg), audio: getAssetAudioUrl(s3Assets.యదAudio), - singleAudio: getAssetAudioUrl(s3Assets.యదAudio), + singleAudio: getAssetAudioUrl(s3Assets.యదSingleAudio), }, { - id: 70, + id: 73, title: "హల్లులు", letters: "య", letter: "య", word: "కాయలు", image: getAssetUrl(s3Assets.కాయలుImg), audio: getAssetAudioUrl(s3Assets.కాయలుAudio), - singleAudio: getAssetAudioUrl(s3Assets.కాయలుAudio), + singleAudio: getAssetAudioUrl(s3Assets.కాయలుSingleAudio), }, { - id: 71, + id: 74, title: "హల్లులు", letters: "య", letter: "య", word: "వంకాయ", image: getAssetUrl(s3Assets.వంకాయImg), audio: getAssetAudioUrl(s3Assets.వంకాయAudio), - singleAudio: getAssetAudioUrl(s3Assets.వంకాయAudio), + singleAudio: getAssetAudioUrl(s3Assets.వంకాయSingleAudio), }, ], }, @@ -4320,34 +4466,34 @@ const dataTe = [ letter: "ర", items: [ { - id: 72, + id: 75, title: "హల్లులు", letters: "ర", letter: "ర", word: "రవి", image: getAssetUrl(s3Assets.రవిImg), audio: getAssetAudioUrl(s3Assets.రవిAudio), - singleAudio: getAssetAudioUrl(s3Assets.రవిAudio), + singleAudio: getAssetAudioUrl(s3Assets.రవిSingleAudio), }, { - id: 73, + id: 76, title: "హల్లులు", letters: "ర", letter: "ర", word: "గిరక", image: getAssetUrl(s3Assets.గిరకImg), audio: getAssetAudioUrl(s3Assets.గిరకAudio), - singleAudio: getAssetAudioUrl(s3Assets.గిరకAudio), + singleAudio: getAssetAudioUrl(s3Assets.గిరకSingleAudio), }, { - id: 74, + id: 77, title: "హల్లులు", letters: "ర", letter: "ర", word: "చీర", image: getAssetUrl(s3Assets.చీరImg), audio: getAssetAudioUrl(s3Assets.చీరAudio), - singleAudio: getAssetAudioUrl(s3Assets.చీరAudio), + singleAudio: getAssetAudioUrl(s3Assets.చీరSingleAudio), }, ], }, @@ -4355,34 +4501,34 @@ const dataTe = [ letter: "ల", items: [ { - id: 75, + id: 78, title: "హల్లులు", letters: "ల", letter: "ల", word: "లత", image: getAssetUrl(s3Assets.లతImg), audio: getAssetAudioUrl(s3Assets.లతAudio), - singleAudio: getAssetAudioUrl(s3Assets.లతAudio), + singleAudio: getAssetAudioUrl(s3Assets.లతSingleAudio), }, { - id: 76, + id: 79, title: "హల్లులు", letters: "ల", letter: "ల", word: "బలపం", image: getAssetUrl(s3Assets.బలపంImg), audio: getAssetAudioUrl(s3Assets.బలపంAudio), - singleAudio: getAssetAudioUrl(s3Assets.బలపంAudio), + singleAudio: getAssetAudioUrl(s3Assets.బలపంSingleAudio), }, { - id: 77, + id: 80, title: "హల్లులు", letters: "ల", letter: "ల", word: "వెల", image: getAssetUrl(s3Assets.వెలImg), audio: getAssetAudioUrl(s3Assets.వెలAudio), - singleAudio: getAssetAudioUrl(s3Assets.వెలAudio), + singleAudio: getAssetAudioUrl(s3Assets.వెలSingleAudio), }, ], }, @@ -4390,34 +4536,34 @@ const dataTe = [ letter: "వ", items: [ { - id: 78, + id: 81, title: "హల్లులు", letters: "వ", letter: "వ", word: "వల", image: getAssetUrl(s3Assets.వలImg), audio: getAssetAudioUrl(s3Assets.వలAudio), - singleAudio: getAssetAudioUrl(s3Assets.వలAudio), + singleAudio: getAssetAudioUrl(s3Assets.వలSingleAudio), }, { - id: 79, + id: 82, title: "హల్లులు", letters: "వ", letter: "వ", word: "లవణం", image: getAssetUrl(s3Assets.లవణంImg), audio: getAssetAudioUrl(s3Assets.లవణంAudio), - singleAudio: getAssetAudioUrl(s3Assets.లవణంAudio), + singleAudio: getAssetAudioUrl(s3Assets.లవణంSingleAudio), }, { - id: 80, + id: 83, title: "హల్లులు", letters: "వ", letter: "వ", word: "పడవ", image: getAssetUrl(s3Assets.పడవImg), audio: getAssetAudioUrl(s3Assets.పడవAudio), - singleAudio: getAssetAudioUrl(s3Assets.పడవAudio), + singleAudio: getAssetAudioUrl(s3Assets.పడవSingleAudio), }, ], }, @@ -4425,34 +4571,34 @@ const dataTe = [ letter: "శ", items: [ { - id: 81, + id: 84, title: "హల్లులు", letters: "శ", letter: "శ", word: "శకటం", image: getAssetUrl(s3Assets.శకటంImg), audio: getAssetAudioUrl(s3Assets.శకటంAudio), - singleAudio: getAssetAudioUrl(s3Assets.శకటంAudio), + singleAudio: getAssetAudioUrl(s3Assets.శకటంSingleAudio), }, { - id: 82, + id: 85, title: "హల్లులు", letters: "శ", letter: "శ", word: "దశమి", image: getAssetUrl(s3Assets.దశమిImg), audio: getAssetAudioUrl(s3Assets.దశమిAudio), - singleAudio: getAssetAudioUrl(s3Assets.దశమిAudio), + singleAudio: getAssetAudioUrl(s3Assets.దశమిSingleAudio), }, { - id: 83, + id: 86, title: "హల్లులు", letters: "శ", letter: "శ", word: "దిశ", image: getAssetUrl(s3Assets.దిశImg), audio: getAssetAudioUrl(s3Assets.దిశAudio), - singleAudio: getAssetAudioUrl(s3Assets.దిశAudio), + singleAudio: getAssetAudioUrl(s3Assets.దిశSingleAudio), }, ], }, @@ -4460,24 +4606,24 @@ const dataTe = [ letter: "ష", items: [ { - id: 84, + id: 87, title: "హల్లులు", letters: "ష", letter: "ష", word: "షరాయి", image: getAssetUrl(s3Assets.షరాయిImg), audio: getAssetAudioUrl(s3Assets.షరాయిAudio), - singleAudio: getAssetAudioUrl(s3Assets.షరాయిAudio), + singleAudio: getAssetAudioUrl(s3Assets.షరాయిSingleAudio), }, { - id: 85, + id: 88, title: "హల్లులు", letters: "ష", letter: "ష", word: "ఉష", image: getAssetUrl(s3Assets.ఉషImg), audio: getAssetAudioUrl(s3Assets.ఉషAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఉషAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఉషSingleAudio), }, ], }, @@ -4485,34 +4631,34 @@ const dataTe = [ letter: "స", items: [ { - id: 86, + id: 89, title: "హల్లులు", letters: "స", letter: "స", word: "సంత", image: getAssetUrl(s3Assets.సంతImg), audio: getAssetAudioUrl(s3Assets.సంతAudio), - singleAudio: getAssetAudioUrl(s3Assets.సంతAudio), + singleAudio: getAssetAudioUrl(s3Assets.సంతSingleAudio), }, { - id: 87, + id: 90, title: "హల్లులు", letters: "స", letter: "స", word: "దసరా", image: getAssetUrl(s3Assets.దసరాImg), audio: getAssetAudioUrl(s3Assets.దసరాAudio), - singleAudio: getAssetAudioUrl(s3Assets.దసరాAudio), + singleAudio: getAssetAudioUrl(s3Assets.దసరాSingleAudio), }, { - id: 88, + id: 91, title: "హల్లులు", letters: "స", letter: "స", word: "పనస", image: getAssetUrl(s3Assets.పనసImg), audio: getAssetAudioUrl(s3Assets.పనసAudio), - singleAudio: getAssetAudioUrl(s3Assets.పనసAudio), + singleAudio: getAssetAudioUrl(s3Assets.పనసSingleAudio), }, ], }, @@ -4520,34 +4666,34 @@ const dataTe = [ letter: "హ", items: [ { - id: 89, + id: 92, title: "హల్లులు", letters: "హ", letter: "హ", word: "హంస", image: getAssetUrl(s3Assets.హంసImg), audio: getAssetAudioUrl(s3Assets.హంసAudio), - singleAudio: getAssetAudioUrl(s3Assets.హంసAudio), + singleAudio: getAssetAudioUrl(s3Assets.హంసSingleAudio), }, { - id: 90, + id: 93, title: "హల్లులు", letters: "హ", letter: "హ", word: "వాహనం", image: getAssetUrl(s3Assets.వాహనంImg), audio: getAssetAudioUrl(s3Assets.వాహనంAudio), - singleAudio: getAssetAudioUrl(s3Assets.వాహనంAudio), + singleAudio: getAssetAudioUrl(s3Assets.వాహనంSingleAudio), }, { - id: 91, + id: 94, title: "హల్లులు", letters: "హ", letter: "హ", word: "గుహ", image: getAssetUrl(s3Assets.గుహImg), audio: getAssetAudioUrl(s3Assets.గుహAudio), - singleAudio: getAssetAudioUrl(s3Assets.గుహAudio), + singleAudio: getAssetAudioUrl(s3Assets.గుహSingleAudio), }, ], }, @@ -4555,24 +4701,24 @@ const dataTe = [ letter: "ళ", items: [ { - id: 92, + id: 95, title: "హల్లులు", letters: "ళ", letter: "ళ", word: "తాళం", image: getAssetUrl(s3Assets.తాళంImg), audio: getAssetAudioUrl(s3Assets.తాళంAudio), - singleAudio: getAssetAudioUrl(s3Assets.తాళంAudio), + singleAudio: getAssetAudioUrl(s3Assets.తాళంSingleAudio), }, { - id: 93, + id: 96, title: "హల్లులు", letters: "ళ", letter: "ళ", word: "కళ", image: getAssetUrl(s3Assets.కళImg), audio: getAssetAudioUrl(s3Assets.కళAudio), - singleAudio: getAssetAudioUrl(s3Assets.కళAudio), + singleAudio: getAssetAudioUrl(s3Assets.కళSingleAudio), }, ], }, @@ -4580,34 +4726,34 @@ const dataTe = [ letter: "క్ష", items: [ { - id: 94, + id: 97, title: "హల్లులు", letters: "క్ష", letter: "క్ష", word: "క్షత్రియుడు", image: getAssetUrl(s3Assets.క్షత్రియుడుImg), audio: getAssetAudioUrl(s3Assets.క్షత్రియుడుAudio), - singleAudio: getAssetAudioUrl(s3Assets.క్షత్రియుడుAudio), + singleAudio: getAssetAudioUrl(s3Assets.క్షత్రియుడుSingleAudio), }, { - id: 95, + id: 98, title: "హల్లులు", letters: "క్ష", letter: "క్ష", word: "అక్షరం", image: getAssetUrl(s3Assets.అక్షరంImg), audio: getAssetAudioUrl(s3Assets.అక్షరంAudio), - singleAudio: getAssetAudioUrl(s3Assets.అక్షరంAudio), + singleAudio: getAssetAudioUrl(s3Assets.అక్షరంSingleAudio), }, { - id: 96, + id: 99, title: "హల్లులు", letters: "క్ష", letter: "క్ష", word: "పరీక్ష", image: getAssetUrl(s3Assets.పరీక్షImg), audio: getAssetAudioUrl(s3Assets.పరీక్షAudio), - singleAudio: getAssetAudioUrl(s3Assets.పరీక్షAudio), + singleAudio: getAssetAudioUrl(s3Assets.పరీక్షSingleAudio), }, ], }, @@ -4615,14 +4761,14 @@ const dataTe = [ letter: "ఱ", items: [ { - id: 97, + id: 100, title: "హల్లులు", letters: "ఱ", letter: "ఱ", word: "ఱంపం", image: getAssetUrl(s3Assets.ఱంపంImg), audio: getAssetAudioUrl(s3Assets.ఱంపంAudio), - singleAudio: getAssetAudioUrl(s3Assets.ఱంపంAudio), + singleAudio: getAssetAudioUrl(s3Assets.ఱంపంSingleAudio), }, ], }, @@ -4660,6 +4806,7 @@ const R0 = ({ currentImg, vocabCount, wordCount, + customLetters, // Array of letters to filter (e.g., ["a", "m", "s", "t"]) //isNextButtonCalled, //setIsNextButtonCalled, }) => { @@ -4679,6 +4826,22 @@ const R0 = ({ data = dataEn; // fallback (English) } + // Filter data based on customLetters if provided + if ( + customLetters && + Array.isArray(customLetters) && + customLetters.length > 0 + ) { + // Normalize customLetters to uppercase for comparison + const normalizedCustomLetters = customLetters.map((letter) => + letter.toUpperCase() + ); + data = data.filter((letterObj) => { + // Check if the letter (uppercase) matches any of the custom letters + return normalizedCustomLetters.includes(letterObj.letter.toUpperCase()); + }); + } + const generatePlaylist = (data) => { const playlist = []; @@ -4785,13 +4948,19 @@ const R0 = ({ if (currentIndex < playlist.length - 1) { setCurrentIndex((i) => i + 1); } else { - setLocalData("rStepZero", 1); - if (process.env.REACT_APP_IS_APP_IFRAME === "true") { - navigate("/"); + // If handleNext prop is provided (e.g., from F1), use it instead of default navigation + if (handleNext && typeof handleNext === "function") { + handleNext(); } else { - navigate("/discover-start"); + // Default R0 behavior + setLocalData("rStepZero", 1); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + console.log("finished r0"); } - console.log("finished r0"); } setRecAudio(null); setIsNextButtonCalled(true); @@ -4912,16 +5081,25 @@ const R0 = ({ let TOTAL_ITEMS = 0; - if (lang === "en") { - TOTAL_ITEMS = 101; - } else if (lang === "hi") { - TOTAL_ITEMS = 151; - } else if (lang === "te") { - TOTAL_ITEMS = 146; - } else if (lang === "kn") { - TOTAL_ITEMS = 142; + // Use actual playlist length if customLetters is provided, otherwise use hardcoded values + if ( + customLetters && + Array.isArray(customLetters) && + customLetters.length > 0 + ) { + TOTAL_ITEMS = playlist.length; } else { - TOTAL_ITEMS = 100; // fallback default + if (lang === "en") { + TOTAL_ITEMS = 101; + } else if (lang === "hi") { + TOTAL_ITEMS = 151; + } else if (lang === "te") { + TOTAL_ITEMS = 146; + } else if (lang === "kn") { + TOTAL_ITEMS = 142; + } else { + TOTAL_ITEMS = 100; // fallback default + } } // FIXED: Use currentIndex + 1 instead of item?.id @@ -5031,9 +5209,9 @@ const R0 = ({ > {ch} @@ -5138,7 +5316,7 @@ const R0 = ({ fontWeight: 500, fontSize: { xs: 50, md: 75 }, lineHeight: 1, - fontFamily: "Quicksand", + fontFamily: getFontFamily(lang), flex: 1, textAlign: "center", p: 0.2, @@ -5198,11 +5376,11 @@ const R0 = ({ {renderHighlightedWord(item.word, item.letter)} @@ -5276,11 +5454,20 @@ const R0 = ({ //console.log("ui2"); let TOTAL_ITEMS = 0; - if (lang === "en") TOTAL_ITEMS = 101; - else if (lang === "kn") TOTAL_ITEMS = 142; - else if (lang === "hi") TOTAL_ITEMS = 151; - else if (lang === "te") TOTAL_ITEMS = 146; - else TOTAL_ITEMS = 100; // fallback + // Use actual playlist length if customLetters is provided, otherwise use hardcoded values + if ( + customLetters && + Array.isArray(customLetters) && + customLetters.length > 0 + ) { + TOTAL_ITEMS = playlist.length; + } else { + if (lang === "en") TOTAL_ITEMS = 101; + else if (lang === "kn") TOTAL_ITEMS = 142; + else if (lang === "hi") TOTAL_ITEMS = 151; + else if (lang === "te") TOTAL_ITEMS = 146; + else TOTAL_ITEMS = 100; // fallback + } const currentItemNumber = currentIndex + 1; const completionPercentage = Math.round( (currentItemNumber / TOTAL_ITEMS) * 100 diff --git a/src/RFlow/R1.jsx b/src/RFlow/R1.jsx index 97469024..f4126afb 100644 --- a/src/RFlow/R1.jsx +++ b/src/RFlow/R1.jsx @@ -23,6 +23,7 @@ import { setLocalData, sendTestRigScore, } from "../utils/constants"; +import { getFontFamily } from "../utils/fontUtils"; import { useNavigate } from "react-router-dom"; import { response } from "../services/telementryService"; import { Typography, Stack, IconButton } from "@mui/material"; @@ -40,7 +41,10 @@ import { fetchUserPoints, createLearnerProgress, } from "../services/orchestration/orchestrationService"; -import { fetchGetSetResult } from "../services/learnerAi/learnerAiService"; +import { + fetchGetSetResult, + callEngagementPredictor, +} from "../services/learnerAi/learnerAiService"; import { fetchAssessmentData, fetchPaginatedContent, @@ -996,6 +1000,10 @@ const R1 = ({ totalSyllableCount ); console.log("GetSet result:", getSetResultRes); + + // Call engagement predictor after getsetresult + // Interactions and lesson are automatically retrieved + callEngagementPredictor(sub_session_id); } catch (error) { console.error("Error fetching set result:", error); } @@ -1289,9 +1297,9 @@ const R1 = ({ > {ch} @@ -1318,7 +1326,8 @@ const R1 = ({ sx={{ color: red, fontWeight: 500, - fontSize: { xs: 120, md: 160 }, + fontSize: + lang === "te" ? { xs: 135, md: 180 } : { xs: 120, md: 160 }, lineHeight: 1, ml: { xs: 1, md: 2 }, fontFamily: "Quicksand", @@ -1350,13 +1359,13 @@ const R1 = ({ /> {renderHighlightedWord(item?.word, item?.letter)} diff --git a/src/RFlow/R2.jsx b/src/RFlow/R2.jsx index ac649e97..3c55ae3a 100644 --- a/src/RFlow/R2.jsx +++ b/src/RFlow/R2.jsx @@ -25,6 +25,7 @@ import { RetryIcon, setLocalData, } from "../utils/constants"; +import { getFontFamily } from "../utils/fontUtils"; import { useNavigate } from "react-router-dom"; import chairImg from "../assets/chair.svg"; import correctTick from "../assets/correctTick.svg"; diff --git a/src/RFlow/R4.jsx b/src/RFlow/R4.jsx index b9355654..1e67d84e 100644 --- a/src/RFlow/R4.jsx +++ b/src/RFlow/R4.jsx @@ -448,7 +448,7 @@ const R4 = ({ flexDirection: "column", alignItems: "center", justifyContent: "center", - height: "97vh", + height: "70vh", background: "linear-gradient(180deg, #91E7EF 0%, #42C6FF 100%)", padding: "16px", position: "relative", @@ -691,7 +691,7 @@ const R4 = ({
{ + onClick={async () => { const audio = new Audio(correctSound); audio.play(); setRecording("no"); @@ -705,7 +705,16 @@ const R4 = ({ if (process.env.REACT_APP_IS_APP_IFRAME === "true") { navigate("/"); } else { - navigate("/discover-start"); + // If handleNext prop is provided (e.g., from Practice flow), use it to update progress + if (handleNext && typeof handleNext === "function") { + // Call handleNext(true) to indicate mechanism is complete and trigger progress update + await handleNext(true); + navigate("/discover-start"); + return; + } else { + // Standalone R flow mode - navigate to discover-start + navigate("/discover-start"); + } } } else { setCurrentQuestionIndex((prevIndex) => prevIndex + 1); @@ -749,7 +758,7 @@ const R4 = ({ > { + onClick={async () => { const audio = new Audio(correctSound); audio.play(); setRecording("no"); @@ -762,7 +771,15 @@ const R4 = ({ if (process.env.REACT_APP_IS_APP_IFRAME === "true") { navigate("/"); } else { - navigate("/discover-start"); + // If handleNext prop is provided (e.g., from Practice flow), use it to update progress + if (handleNext && typeof handleNext === "function") { + // Call handleNext(true) to indicate mechanism is complete and trigger progress update + await handleNext(true); + return; + } else { + // Standalone R flow mode - navigate to discover-start + navigate("/discover-start"); + } } } else { setCurrentQuestionIndex((prevIndex) => prevIndex + 1); diff --git a/src/RFlow/SoundHunt.jsx b/src/RFlow/SoundHunt.jsx new file mode 100644 index 00000000..56cfef6a --- /dev/null +++ b/src/RFlow/SoundHunt.jsx @@ -0,0 +1,3318 @@ +import React, { useState, useEffect, useMemo } from "react"; +import Confetti from "react-confetti"; +import * as Assets from "../utils/imageAudioLinks"; +import * as s3Assets from "../utils/s3Links"; +import { getAssetUrl } from "../utils/s3Links"; +import { getAssetAudioUrl } from "../utils/s3Links"; +import { + ThemeProvider, + createTheme, + useMediaQuery, + Grid, + Box, +} from "@mui/material"; +import MainLayout from "../components/Layouts.jsx/MainLayout"; +import listenImg from "../assets/listen.png"; +// import Mic from "../assets/mikee.svg"; +// import Stop from "../assets/pausse.svg"; +import correctSound from "../assets/correct.wav"; +import wrongSound from "../assets/audio/wrong.wav"; +import RecordVoiceVisualizer from "../utils/RecordVoiceVisualizer"; +import { + practiceSteps, + getLocalData, + NextButtonRound, + RetryIcon, + setLocalData, +} from "../utils/constants"; +import { useNavigate } from "react-router-dom"; + +const theme = createTheme(); + +const content = { + en: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.boatImg), + text: "boat", + audio: getAssetAudioUrl(s3Assets.boatAudio), + }, + { + img: getAssetUrl(s3Assets.hotImg), + text: "hot", + audio: getAssetAudioUrl(s3Assets.hotAudio), + }, + { + img: getAssetUrl(s3Assets.coatImg), + text: "coat", + audio: getAssetAudioUrl(s3Assets.coatAudio), + }, + ], + correctWord: "hot", + audio: getAssetAudioUrl(s3Assets.hotAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.boatImg), + text: "boat", + audio: getAssetAudioUrl(s3Assets.boatAudio), + }, + { + img: getAssetUrl(s3Assets.toadImg), + text: "toad", + audio: getAssetAudioUrl(s3Assets.toadAudio), + }, + { + img: getAssetUrl(s3Assets.bikeImg), + text: "bike", + audio: getAssetAudioUrl(s3Assets.bikeAudio), + }, + ], + correctWord: "boat", + audio: getAssetAudioUrl(s3Assets.boatAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.catImg), + text: "cat", + audio: getAssetAudioUrl(s3Assets.catAudio), + }, + { + img: getAssetUrl(s3Assets.tapImg), + text: "tap", + audio: getAssetAudioUrl(s3Assets.tapAudio), + }, + { + img: getAssetUrl(s3Assets.coatImg), + text: "coat", + audio: getAssetAudioUrl(s3Assets.coatAudio), + }, + ], + correctWord: "coat", + audio: getAssetAudioUrl(s3Assets.coatAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.matImg), + text: "mat", + audio: getAssetAudioUrl(s3Assets.matAudio), + }, + { + img: getAssetUrl(s3Assets.toadImg), + text: "toad", + audio: getAssetAudioUrl(s3Assets.toadAudio), + }, + { + img: getAssetUrl(s3Assets.potImg), + text: "pot", + audio: getAssetAudioUrl(s3Assets.potAudio), + }, + ], + correctWord: "toad", + audio: getAssetAudioUrl(s3Assets.toadAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.bikeImg), + text: "bike", + audio: getAssetAudioUrl(s3Assets.bikeAudio), + }, + { + img: getAssetUrl(s3Assets.godImg), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio), + }, + { + img: getAssetUrl(s3Assets.hotImg), + text: "hot", + audio: getAssetAudioUrl(s3Assets.hotAudio), + }, + ], + correctWord: "bike", + audio: getAssetAudioUrl(s3Assets.bikeAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.boatImg), + text: "boat", + audio: getAssetAudioUrl(s3Assets.boatAudio), + }, + { + img: getAssetUrl(s3Assets.coatImg), + text: "coat", + audio: getAssetAudioUrl(s3Assets.coatAudio), + }, + { + img: getAssetUrl(s3Assets.catImg), + text: "cat", + audio: getAssetAudioUrl(s3Assets.catAudio), + }, + ], + correctWord: "cat", + audio: getAssetAudioUrl(s3Assets.catAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.toadImg), + text: "toad", + audio: getAssetAudioUrl(s3Assets.toadAudio), + }, + { + img: getAssetUrl(s3Assets.tapImg), + text: "tap", + audio: getAssetAudioUrl(s3Assets.tapAudio), + }, + { + img: getAssetUrl(s3Assets.bikeImg), + text: "bike", + audio: getAssetAudioUrl(s3Assets.bikeAudio), + }, + ], + correctWord: "tap", + audio: getAssetAudioUrl(s3Assets.tapAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.matImg), + text: "mat", + audio: getAssetAudioUrl(s3Assets.matAudio), + }, + { + img: getAssetUrl(s3Assets.catImg), + text: "cat", + audio: getAssetAudioUrl(s3Assets.catAudio), + }, + { + img: getAssetUrl(s3Assets.potImg), + text: "pot", + audio: getAssetAudioUrl(s3Assets.potAudio), + }, + ], + correctWord: "mat", + audio: getAssetAudioUrl(s3Assets.matAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.godImg), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio), + }, + { + img: getAssetUrl(s3Assets.hotImg), + text: "hot", + audio: getAssetAudioUrl(s3Assets.hotAudio), + }, + { + img: getAssetUrl(s3Assets.potImg), + text: "pot", + audio: getAssetAudioUrl(s3Assets.potAudio), + }, + ], + correctWord: "pot", + audio: getAssetAudioUrl(s3Assets.potAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.boatImg), + text: "boat", + audio: getAssetAudioUrl(s3Assets.boatAudio), + }, + { + img: getAssetUrl(s3Assets.godImg), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio), + }, + { + img: getAssetUrl(s3Assets.coatImg), + text: "coat", + audio: getAssetAudioUrl(s3Assets.coatAudio), + }, + ], + correctWord: "god", + audio: getAssetAudioUrl(s3Assets.godAudio), + flowName: "P1", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.toeImg), + text: "toe", + audio: getAssetAudioUrl(s3Assets.toeAudio), + }, + { + img: getAssetUrl(s3Assets.binImg), + text: "bin", + audio: getAssetAudioUrl(s3Assets.binAudio), + }, + { + img: getAssetUrl(s3Assets.packImg), + text: "pack", + audio: getAssetAudioUrl(s3Assets.packAudio), + }, + ], + correctWord: "toe", + audio: getAssetAudioUrl(s3Assets.toeAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.tieImg), + text: "tie", + audio: getAssetAudioUrl(s3Assets.tieAudio), + }, + { + img: getAssetUrl(s3Assets.pineImg), + text: "pine", + audio: getAssetAudioUrl(s3Assets.pineAudio), + }, + { + img: getAssetUrl(s3Assets.timeImg), + text: "time", + audio: getAssetAudioUrl(s3Assets.timeAudio), + }, + ], + correctWord: "time", + audio: getAssetAudioUrl(s3Assets.timeAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.tieImg), + text: "tie", + audio: getAssetAudioUrl(s3Assets.tieAudio), + }, + { + img: getAssetUrl(s3Assets.palmImg), + text: "palm", + audio: getAssetAudioUrl(s3Assets.palmAudio), + }, + { + img: getAssetUrl(s3Assets.pineImg), + text: "pine", + audio: getAssetAudioUrl(s3Assets.pineAudio), + }, + ], + correctWord: "pine", + audio: getAssetAudioUrl(s3Assets.pineAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.nightImg), + text: "night", + audio: getAssetAudioUrl(s3Assets.nightAudio), + }, + { + img: getAssetUrl(s3Assets.binImg), + text: "bin", + audio: getAssetAudioUrl(s3Assets.binAudio), + }, + { + img: getAssetUrl(s3Assets.tieImg), + text: "tie", + audio: getAssetAudioUrl(s3Assets.tieAudio), + }, + ], + correctWord: "tie", + audio: getAssetAudioUrl(s3Assets.tieAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.nightImg), + text: "night", + audio: getAssetAudioUrl(s3Assets.nightAudio), + }, + { + img: getAssetUrl(s3Assets.binImg), + text: "bin", + audio: getAssetAudioUrl(s3Assets.binAudio), + }, + { + img: getAssetUrl(s3Assets.pineImg), + text: "pine", + audio: getAssetAudioUrl(s3Assets.pineAudio), + }, + ], + correctWord: "bin", + audio: getAssetAudioUrl(s3Assets.binAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.toeImg), + text: "toe", + audio: getAssetAudioUrl(s3Assets.toeAudio), + }, + { + img: getAssetUrl(s3Assets.packImg), + text: "pack", + audio: getAssetAudioUrl(s3Assets.packAudio), + }, + { + img: getAssetUrl(s3Assets.pondImg), + text: "pond", + audio: getAssetAudioUrl(s3Assets.pondAudio), + }, + ], + correctWord: "pack", + audio: getAssetAudioUrl(s3Assets.packAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.palmImg), + text: "palm", + audio: getAssetAudioUrl(s3Assets.palmAudio), + }, + { + img: getAssetUrl(s3Assets.binImg), + text: "bin", + audio: getAssetAudioUrl(s3Assets.binAudio), + }, + { + img: getAssetUrl(s3Assets.pineImg), + text: "pine", + audio: getAssetAudioUrl(s3Assets.pineAudio), + }, + ], + correctWord: "palm", + audio: getAssetAudioUrl(s3Assets.palmAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.binImg), + text: "bin", + audio: getAssetAudioUrl(s3Assets.binAudio), + }, + { + img: getAssetUrl(s3Assets.pondImg), + text: "pond", + audio: getAssetAudioUrl(s3Assets.pondAudio), + }, + { + img: getAssetUrl(s3Assets.pitImg), + text: "pit", + audio: getAssetAudioUrl(s3Assets.pitAudio), + }, + ], + correctWord: "pond", + audio: getAssetAudioUrl(s3Assets.pondAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.palmImg), + text: "palm", + audio: getAssetAudioUrl(s3Assets.palmAudio), + }, + { + img: getAssetUrl(s3Assets.nightImg), + text: "night", + audio: getAssetAudioUrl(s3Assets.nightAudio), + }, + { + img: getAssetUrl(s3Assets.pitImg), + text: "pit", + audio: getAssetAudioUrl(s3Assets.pitAudio), + }, + ], + correctWord: "pit", + audio: getAssetAudioUrl(s3Assets.pitAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.pitImg), + text: "pit", + audio: getAssetAudioUrl(s3Assets.pitAudio), + }, + { + img: getAssetUrl(s3Assets.timeImg), + text: "time", + audio: getAssetAudioUrl(s3Assets.timeAudio), + }, + { + img: getAssetUrl(s3Assets.nightImg), + text: "night", + audio: getAssetAudioUrl(s3Assets.nightAudio), + }, + ], + correctWord: "night", + audio: getAssetAudioUrl(s3Assets.nightAudio), + flowName: "P3", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.mathImg), + text: "math", + audio: getAssetAudioUrl(s3Assets.mathAudio), + }, + { + img: getAssetUrl(s3Assets.breatheImg), + text: "breathe", + audio: getAssetAudioUrl(s3Assets.breatheAudio), + }, + { + img: getAssetUrl(s3Assets.jumpImg), + text: "jump", + audio: getAssetAudioUrl(s3Assets.jumpAudio), + }, + ], + correctWord: "jump", + audio: getAssetAudioUrl(s3Assets.jumpAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.mathImg), + text: "math", + audio: getAssetAudioUrl(s3Assets.mathAudio), + }, + { + img: getAssetUrl(s3Assets.singImg), + text: "sing", + audio: getAssetAudioUrl(s3Assets.singAudio), + }, + { + img: getAssetUrl(s3Assets.breatheImg), + text: "breathe", + audio: getAssetAudioUrl(s3Assets.breatheAudio), + }, + ], + correctWord: "sing", + audio: getAssetAudioUrl(s3Assets.singAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ringImg), + text: "ring", + audio: getAssetAudioUrl(s3Assets.ringAudio), + }, + { + img: getAssetUrl(s3Assets.jumpImg), + text: "jump", + audio: getAssetAudioUrl(s3Assets.jumpAudio), + }, + { + img: getAssetUrl(s3Assets.mathImg), + text: "math", + audio: getAssetAudioUrl(s3Assets.mathAudio), + }, + ], + correctWord: "ring", + audio: getAssetAudioUrl(s3Assets.ringAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.pathImg), + text: "path", + audio: getAssetAudioUrl(s3Assets.pathAudio), + }, + { + img: getAssetUrl(s3Assets.wingImg), + text: "wing", + audio: getAssetAudioUrl(s3Assets.wingAudio), + }, + { + img: getAssetUrl(s3Assets.singImg), + text: "sing", + audio: getAssetAudioUrl(s3Assets.singAudio), + }, + ], + correctWord: "wing", + audio: getAssetAudioUrl(s3Assets.wingAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.pathImg), + text: "path", + audio: getAssetAudioUrl(s3Assets.pathAudio), + }, + { + img: getAssetUrl(s3Assets.singImg), + text: "sing", + audio: getAssetAudioUrl(s3Assets.singAudio), + }, + { + img: getAssetUrl(s3Assets.mathImg), + text: "math", + audio: getAssetAudioUrl(s3Assets.mathAudio), + }, + ], + correctWord: "path", + audio: getAssetAudioUrl(s3Assets.pathAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.mathImg), + text: "math", + audio: getAssetAudioUrl(s3Assets.mathAudio), + }, + { + img: getAssetUrl(s3Assets.ringImg), + text: "ring", + audio: getAssetAudioUrl(s3Assets.ringAudio), + }, + { + img: getAssetUrl(s3Assets.breatheImg), + text: "breathe", + audio: getAssetAudioUrl(s3Assets.breatheAudio), + }, + ], + correctWord: "math", + audio: getAssetAudioUrl(s3Assets.mathAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.wingImg), + text: "wing", + audio: getAssetAudioUrl(s3Assets.wingAudio), + }, + { + img: getAssetUrl(s3Assets.furImg), + text: "fur", + audio: getAssetAudioUrl(s3Assets.furAudio), + }, + { + img: getAssetUrl(s3Assets.breatheImg), + text: "breathe", + audio: getAssetAudioUrl(s3Assets.breatheAudio), + }, + ], + correctWord: "breathe", + audio: getAssetAudioUrl(s3Assets.breatheAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ringImg), + text: "ring", + audio: getAssetAudioUrl(s3Assets.ringAudio), + }, + { + img: getAssetUrl(s3Assets.runImg), + text: "run", + audio: getAssetAudioUrl(s3Assets.runAudio), + }, + { + img: getAssetUrl(s3Assets.jumpImg), + text: "jump", + audio: getAssetAudioUrl(s3Assets.jumpAudio), + }, + ], + correctWord: "run", + audio: getAssetAudioUrl(s3Assets.runAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ringImg), + text: "ring", + audio: getAssetAudioUrl(s3Assets.ringAudio), + }, + { + img: getAssetUrl(s3Assets.singImg), + text: "sing", + audio: getAssetAudioUrl(s3Assets.singAudio), + }, + { + img: getAssetUrl(s3Assets.birdImg), + text: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio), + }, + ], + correctWord: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.furImg), + text: "fur", + audio: getAssetAudioUrl(s3Assets.furAudio), + }, + { + img: getAssetUrl(s3Assets.pathImg), + text: "path", + audio: getAssetAudioUrl(s3Assets.pathAudio), + }, + { + img: getAssetUrl(s3Assets.birdImg), + text: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio), + }, + ], + correctWord: "fur", + audio: getAssetAudioUrl(s3Assets.furAudio), + flowName: "P2", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.threeImg2), + text: "three", + audio: getAssetAudioUrl(s3Assets.threeAudio), + }, + { + img: getAssetUrl(s3Assets.riverImg), + text: "river", + audio: getAssetAudioUrl(s3Assets.riverAudio), + }, + { + img: getAssetUrl(s3Assets.thumbImg), + text: "thumb", + audio: getAssetAudioUrl(s3Assets.thumbAudio), + }, + ], + correctWord: "thumb", + audio: getAssetAudioUrl(s3Assets.thumbAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.earthImg), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio), + }, + { + img: getAssetUrl(s3Assets.magicImg), + text: "magic", + audio: getAssetAudioUrl(s3Assets.magicAudio), + }, + { + img: getAssetUrl(s3Assets.mother2Img), + text: "mother", + audio: getAssetAudioUrl(s3Assets.motherAudio), + }, + ], + correctWord: "mother", + audio: getAssetAudioUrl(s3Assets.motherAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.fatherImg), + text: "father", + audio: getAssetAudioUrl(s3Assets.fatherAudio), + }, + { + img: getAssetUrl(s3Assets.dinnerImg), + text: "dinner", + audio: getAssetAudioUrl(s3Assets.dinner2Audio), + }, + { + img: getAssetUrl(s3Assets.mother2Img), + text: "mother", + audio: getAssetAudioUrl(s3Assets.motherAudio), + }, + ], + correctWord: "father", + audio: getAssetAudioUrl(s3Assets.fatherAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.dinnerImg), + text: "dinner", + audio: getAssetAudioUrl(s3Assets.dinner2Audio), + }, + { + img: getAssetUrl(s3Assets.riverImg), + text: "river", + audio: getAssetAudioUrl(s3Assets.riverAudio), + }, + { + img: getAssetUrl(s3Assets.threeImg2), + text: "three", + audio: getAssetAudioUrl(s3Assets.threeAudio), + }, + ], + correctWord: "three", + audio: getAssetAudioUrl(s3Assets.threeAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.earthImg), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio), + }, + { + img: getAssetUrl(s3Assets.purpleImg), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio), + }, + { + img: getAssetUrl(s3Assets.rabbitImg), + text: "rabbit", + audio: getAssetAudioUrl(s3Assets.rabbitAudio), + }, + ], + correctWord: "rabbit", + audio: getAssetAudioUrl(s3Assets.rabbitAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.magicImg), + text: "magic", + audio: getAssetAudioUrl(s3Assets.magicAudio), + }, + { + img: getAssetUrl(s3Assets.purpleImg), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio), + }, + { + img: getAssetUrl(s3Assets.riverImg), + text: "river", + audio: getAssetAudioUrl(s3Assets.riverAudio), + }, + ], + correctWord: "river", + audio: getAssetAudioUrl(s3Assets.riverAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.purpleImg), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio), + }, + { + img: getAssetUrl(s3Assets.rabbitImg), + text: "rabbit", + audio: getAssetAudioUrl(s3Assets.rabbitAudio), + }, + { + img: getAssetUrl(s3Assets.threeImg2), + text: "three", + audio: getAssetAudioUrl(s3Assets.threeAudio), + }, + ], + correctWord: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.dinnerImg), + text: "dinner", + audio: getAssetAudioUrl(s3Assets.dinner2Audio), + }, + { + img: getAssetUrl(s3Assets.fatherImg), + text: "father", + audio: getAssetAudioUrl(s3Assets.fatherAudio), + }, + { + img: getAssetUrl(s3Assets.purpleImg), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio), + }, + ], + correctWord: "dinner", + audio: getAssetAudioUrl(s3Assets.dinner2Audio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.thumbImg), + text: "thumb", + audio: getAssetAudioUrl(s3Assets.thumbAudio), + }, + { + img: getAssetUrl(s3Assets.earthImg), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio), + }, + { + img: getAssetUrl(s3Assets.purpleImg), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio), + }, + ], + correctWord: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio), + flowName: "P4", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.magicImg), + text: "magic", + audio: getAssetAudioUrl(s3Assets.magicAudio), + }, + { + img: getAssetUrl(s3Assets.threeImg2), + text: "three", + audio: getAssetAudioUrl(s3Assets.threeAudio), + }, + { + img: getAssetUrl(s3Assets.earthImg), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio), + }, + ], + correctWord: "magic", + audio: getAssetAudioUrl(s3Assets.magicAudio), + flowName: "P4", + }, + ], + te: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + { + img: getAssetUrl(s3Assets.హసImg), + text: "హంస", + audio: getAssetAudioUrl(s3Assets.హసAudio), + }, + { + img: getAssetUrl(s3Assets.పలకImg), + text: "పలక", + audio: getAssetAudioUrl(s3Assets.పలకAudio), + }, + ], + correctWord: "హంస", + audio: getAssetAudioUrl(s3Assets.హసAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బడImg), + text: "బండ", + audio: getAssetAudioUrl(s3Assets.బడAudio), + }, + { + img: getAssetUrl(s3Assets.హసImg), + text: "హంస", + audio: getAssetAudioUrl(s3Assets.హసAudio), + }, + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + ], + correctWord: "బండ", + audio: getAssetAudioUrl(s3Assets.బడAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.నడకImg), + text: "నడక", + audio: getAssetAudioUrl(s3Assets.నడకAudio), + }, + { + img: getAssetUrl(s3Assets.పలకImg), + text: "పలక", + audio: getAssetAudioUrl(s3Assets.పలకAudio), + }, + { + img: getAssetUrl(s3Assets.వనImg), + text: "వనం", + audio: getAssetAudioUrl(s3Assets.వనAudio), + }, + ], + correctWord: "వనం", + audio: getAssetAudioUrl(s3Assets.వనAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.రథImg), + text: "రథం", + audio: getAssetAudioUrl(s3Assets.రథAudio), + }, + { + img: getAssetUrl(s3Assets.తబలImg), + text: "తబల", + audio: getAssetAudioUrl(s3Assets.తబలAudio), + }, + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + ], + correctWord: "రథం", + audio: getAssetAudioUrl(s3Assets.రథAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఫలImg), + text: "ఫలం", + audio: getAssetAudioUrl(s3Assets.ఫలAudio), + }, + { + img: getAssetUrl(s3Assets.బడImg), + text: "బండ", + audio: getAssetAudioUrl(s3Assets.బడAudio), + }, + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + ], + correctWord: "ఫలం", + audio: getAssetAudioUrl(s3Assets.ఫలAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పలకImg), + text: "పలక", + audio: getAssetAudioUrl(s3Assets.పలకAudio), + }, + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + { + img: getAssetUrl(s3Assets.వనImg), + text: "వనం", + audio: getAssetAudioUrl(s3Assets.వనAudio), + }, + ], + correctWord: "పలక", + audio: getAssetAudioUrl(s3Assets.పలకAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.తబలImg), + text: "తబల", + audio: getAssetAudioUrl(s3Assets.తబలAudio), + }, + { + img: getAssetUrl(s3Assets.వనImg), + text: "వనం", + audio: getAssetAudioUrl(s3Assets.వనAudio), + }, + { + img: getAssetUrl(s3Assets.రథImg), + text: "రథం", + audio: getAssetAudioUrl(s3Assets.రథAudio), + }, + ], + correctWord: "తబల", + audio: getAssetAudioUrl(s3Assets.తబలAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.రథImg), + text: "రథం", + audio: getAssetAudioUrl(s3Assets.రథAudio), + }, + { + img: getAssetUrl(s3Assets.బడImg), + text: "బండ", + audio: getAssetAudioUrl(s3Assets.బడAudio), + }, + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + ], + correctWord: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.శనగImg), + text: "శనగ", + audio: getAssetAudioUrl(s3Assets.శనగAudio), + }, + { + img: getAssetUrl(s3Assets.బడImg), + text: "బండ", + audio: getAssetAudioUrl(s3Assets.బడAudio), + }, + { + img: getAssetUrl(s3Assets.నడకImg), + text: "నడక", + audio: getAssetAudioUrl(s3Assets.నడకAudio), + }, + ], + correctWord: "నడక", + audio: getAssetAudioUrl(s3Assets.నడకAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఔషధImg), + text: "ఔషధ", + audio: getAssetAudioUrl(s3Assets.ఔషధAudio), + }, + { + img: getAssetUrl(s3Assets.ఫలImg), + text: "ఫలం", + audio: getAssetAudioUrl(s3Assets.ఫలAudio), + }, + { + img: getAssetUrl(s3Assets.నడకImg), + text: "నడక", + audio: getAssetAudioUrl(s3Assets.నడకAudio), + }, + ], + correctWord: "ఔషధ", + audio: getAssetAudioUrl(s3Assets.ఔషధAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.తనImg), + text: "తేనె", + audio: getAssetAudioUrl(s3Assets.తనAudio), + }, + { + img: getAssetUrl(s3Assets.వరImg), + text: "వేరు", + audio: getAssetAudioUrl(s3Assets.వరAudio), + }, + { + img: getAssetUrl(s3Assets.కదImg), + text: "కింద", + audio: getAssetAudioUrl(s3Assets.కదAudio), + }, + ], + correctWord: "కింద", + audio: getAssetAudioUrl(s3Assets.కదAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.గడImg), + text: "గుడి", + audio: getAssetAudioUrl(s3Assets.గడAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio), + }, + { + img: getAssetUrl(s3Assets.పలImg), + text: "పులి", + audio: getAssetAudioUrl(s3Assets.పలAudio), + }, + ], + correctWord: "గుడి", + audio: getAssetAudioUrl(s3Assets.గడAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వరImg), + text: "వేరు", + audio: getAssetAudioUrl(s3Assets.వరAudio), + }, + { + img: getAssetUrl(s3Assets.మడImg2), + text: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio2), + }, + { + img: getAssetUrl(s3Assets.బవImg), + text: "బావి", + audio: getAssetAudioUrl(s3Assets.బవAudio), + }, + ], + correctWord: "వేరు", + audio: getAssetAudioUrl(s3Assets.వరAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పలImg), + text: "పులి", + audio: getAssetAudioUrl(s3Assets.పలAudio), + }, + { + img: getAssetUrl(s3Assets.గడImg), + text: "గుడి", + audio: getAssetAudioUrl(s3Assets.గడAudio), + }, + { + img: getAssetUrl(s3Assets.దగImg), + text: "దొంగ", + audio: getAssetAudioUrl(s3Assets.దగAudio), + }, + ], + correctWord: "పులి", + audio: getAssetAudioUrl(s3Assets.పలAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బవImg), + text: "బావి", + audio: getAssetAudioUrl(s3Assets.బవAudio), + }, + { + img: getAssetUrl(s3Assets.రణImg), + text: "రాణి", + audio: getAssetAudioUrl(s3Assets.రణAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio), + }, + ], + correctWord: "రాణి", + audio: getAssetAudioUrl(s3Assets.రణAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.తనImg), + text: "తేనె", + audio: getAssetAudioUrl(s3Assets.తనAudio), + }, + { + img: getAssetUrl(s3Assets.కదImg), + text: "కింద", + audio: getAssetAudioUrl(s3Assets.కదAudio), + }, + { + img: getAssetUrl(s3Assets.గడImg), + text: "గుడి", + audio: getAssetAudioUrl(s3Assets.గడAudio), + }, + ], + correctWord: "తేనె", + audio: getAssetAudioUrl(s3Assets.తనAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.మడImg2), + text: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio2), + }, + { + img: getAssetUrl(s3Assets.రణImg), + text: "రాణి", + audio: getAssetAudioUrl(s3Assets.రణAudio), + }, + { + img: getAssetUrl(s3Assets.తనImg), + text: "తేనె", + audio: getAssetAudioUrl(s3Assets.తనAudio), + }, + ], + correctWord: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio2), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కదImg), + text: "కింద", + audio: getAssetAudioUrl(s3Assets.కదAudio), + }, + { + img: getAssetUrl(s3Assets.బవImg), + text: "బావి", + audio: getAssetAudioUrl(s3Assets.బవAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio), + }, + ], + correctWord: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.గడImg), + text: "గుడి", + audio: getAssetAudioUrl(s3Assets.గడAudio), + }, + { + img: getAssetUrl(s3Assets.దగImg), + text: "దొంగ", + audio: getAssetAudioUrl(s3Assets.దగAudio), + }, + { + img: getAssetUrl(s3Assets.మడImg2), + text: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio2), + }, + ], + correctWord: "దొంగ", + audio: getAssetAudioUrl(s3Assets.దగAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.గడImg), + text: "గుడి", + audio: getAssetAudioUrl(s3Assets.గడAudio), + }, + { + img: getAssetUrl(s3Assets.బవImg), + text: "బావి", + audio: getAssetAudioUrl(s3Assets.బవAudio), + }, + { + img: getAssetUrl(s3Assets.రణImg), + text: "రాణి", + audio: getAssetAudioUrl(s3Assets.రణAudio), + }, + ], + correctWord: "బావి", + audio: getAssetAudioUrl(s3Assets.బవAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.నపపImg), + text: "నిప్పు", + audio: getAssetAudioUrl(s3Assets.నపపAudio), + }, + { + img: getAssetUrl(s3Assets.అననImg), + text: "అన్నం", + audio: getAssetAudioUrl(s3Assets.అననAudio), + }, + { + img: getAssetUrl(s3Assets.డబబImg), + text: "డబ్బు", + audio: getAssetAudioUrl(s3Assets.డబబAudio), + }, + ], + correctWord: "నిప్పు", + audio: getAssetAudioUrl(s3Assets.నపపAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.అననImg), + text: "అన్నం", + audio: getAssetAudioUrl(s3Assets.అననAudio), + }, + { + img: getAssetUrl(s3Assets.ధనయImg), + text: "ధాన్యం", + audio: getAssetAudioUrl(s3Assets.ధనయAudio), + }, + { + img: getAssetUrl(s3Assets.బససImg), + text: "బస్సు", + audio: getAssetAudioUrl(s3Assets.బససAudio), + }, + ], + correctWord: "ధాన్యం", + audio: getAssetAudioUrl(s3Assets.ధనయAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ధనయImg), + text: "ధాన్యం", + audio: getAssetAudioUrl(s3Assets.ధనయAudio), + }, + { + img: getAssetUrl(s3Assets.డబబImg), + text: "డబ్బు", + audio: getAssetAudioUrl(s3Assets.డబబAudio), + }, + { + img: getAssetUrl(s3Assets.బససImg), + text: "బస్సు", + audio: getAssetAudioUrl(s3Assets.బససAudio), + }, + ], + correctWord: "బస్సు", + audio: getAssetAudioUrl(s3Assets.బససAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పపపImg), + text: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + }, + { + img: getAssetUrl(s3Assets.బససImg), + text: "బస్సు", + audio: getAssetAudioUrl(s3Assets.బససAudio), + }, + { + img: getAssetUrl(s3Assets.మకకImg), + text: "ముక్కు", + audio: getAssetAudioUrl(s3Assets.మకకAudio), + }, + ], + correctWord: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.డబబImg), + text: "డబ్బు", + audio: getAssetAudioUrl(s3Assets.డబబAudio), + }, + { + img: getAssetUrl(s3Assets.పపపImg), + text: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + }, + { + img: getAssetUrl(s3Assets.బటటImg), + text: "బుట్ట", + audio: getAssetAudioUrl(s3Assets.బటటAudio), + }, + ], + correctWord: "డబ్బు", + audio: getAssetAudioUrl(s3Assets.డబబAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పపపImg), + text: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + }, + { + img: getAssetUrl(s3Assets.బవవImg), + text: "బువ్వ", + audio: getAssetAudioUrl(s3Assets.బవవAudio), + }, + { + img: getAssetUrl(s3Assets.మలలImg), + text: "మల్లె", + audio: getAssetAudioUrl(s3Assets.మలలAudio), + }, + ], + correctWord: "బువ్వ", + audio: getAssetAudioUrl(s3Assets.బవవAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బటటImg), + text: "బుట్ట", + audio: getAssetAudioUrl(s3Assets.బటటAudio), + }, + { + img: getAssetUrl(s3Assets.అననImg), + text: "అన్నం", + audio: getAssetAudioUrl(s3Assets.అననAudio), + }, + { + img: getAssetUrl(s3Assets.పపపImg), + text: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + }, + ], + correctWord: "అన్నం", + audio: getAssetAudioUrl(s3Assets.అననAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.మలలImg), + text: "మల్లె", + audio: getAssetAudioUrl(s3Assets.మలలAudio), + }, + { + img: getAssetUrl(s3Assets.పపపImg), + text: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + }, + { + img: getAssetUrl(s3Assets.అననImg), + text: "అన్నం", + audio: getAssetAudioUrl(s3Assets.అననAudio), + }, + ], + correctWord: "మల్లె", + audio: getAssetAudioUrl(s3Assets.మలలAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.డబబImg), + text: "డబ్బు", + audio: getAssetAudioUrl(s3Assets.డబబAudio), + }, + { + img: getAssetUrl(s3Assets.మకకImg), + text: "ముక్కు", + audio: getAssetAudioUrl(s3Assets.మకకAudio), + }, + { + img: getAssetUrl(s3Assets.బటటImg), + text: "బుట్ట", + audio: getAssetAudioUrl(s3Assets.బటటAudio), + }, + ], + correctWord: "బుట్ట", + audio: getAssetAudioUrl(s3Assets.బటటAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బవవImg), + text: "బువ్వ", + audio: getAssetAudioUrl(s3Assets.బవవAudio), + }, + { + img: getAssetUrl(s3Assets.పపపImg), + text: "పప్పు", + audio: getAssetAudioUrl(s3Assets.పపపAudio), + }, + { + img: getAssetUrl(s3Assets.మకకImg), + text: "ముక్కు", + audio: getAssetAudioUrl(s3Assets.మకకAudio), + }, + ], + correctWord: "ముక్కు", + audio: getAssetAudioUrl(s3Assets.మకకAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పలలలImg), + text: "పుల్లలు", + audio: getAssetAudioUrl(s3Assets.పలలలAudio), + }, + { + img: getAssetUrl(s3Assets.గమమడImg), + text: "గుమ్మడి", + audio: getAssetAudioUrl(s3Assets.గమమడAudio), + }, + { + img: getAssetUrl(s3Assets.మదదబతImg), + text: "ముద్దబంతి", + audio: getAssetAudioUrl(s3Assets.మదదబతAudio), + }, + ], + correctWord: "పుల్లలు", + audio: getAssetAudioUrl(s3Assets.పలలలAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వరణమలImg), + text: "వర్ణమాల", + audio: getAssetAudioUrl(s3Assets.వరణమలAudio), + }, + { + img: getAssetUrl(s3Assets.గమమడImg), + text: "గుమ్మడి", + audio: getAssetAudioUrl(s3Assets.గమమడAudio), + }, + { + img: getAssetUrl(s3Assets.పటటకImg), + text: "పట్టిక", + audio: getAssetAudioUrl(s3Assets.పటటకAudio), + }, + ], + correctWord: "పట్టిక", + audio: getAssetAudioUrl(s3Assets.పటటకAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వరషలImg), + text: "వర్షాలు", + audio: getAssetAudioUrl(s3Assets.వరషలAudio), + }, + { + img: getAssetUrl(s3Assets.బతకమమImg), + text: "బతుకమ్మ", + audio: getAssetAudioUrl(s3Assets.బతకమమAudio), + }, + { + img: getAssetUrl(s3Assets.కయలడరImg), + text: "క్యాలెండర్", + audio: getAssetAudioUrl(s3Assets.కయలడరAudio), + }, + ], + correctWord: "వర్షాలు", + audio: getAssetAudioUrl(s3Assets.వరషలAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.మదదబతImg), + text: "ముద్దబంతి", + audio: getAssetAudioUrl(s3Assets.మదదబతAudio), + }, + { + img: getAssetUrl(s3Assets.గమమడImg), + text: "గుమ్మడి", + audio: getAssetAudioUrl(s3Assets.గమమడAudio), + }, + { + img: getAssetUrl(s3Assets.చలకమమImg), + text: "చిలకమ్మ", + audio: getAssetAudioUrl(s3Assets.చలకమమAudio), + }, + ], + correctWord: "గుమ్మడి", + audio: getAssetAudioUrl(s3Assets.గమమడAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కయలడరImg), + text: "క్యాలెండర్", + audio: getAssetAudioUrl(s3Assets.కయలడరAudio), + }, + { + img: getAssetUrl(s3Assets.బతకమమImg), + text: "బతుకమ్మ", + audio: getAssetAudioUrl(s3Assets.బతకమమAudio), + }, + { + img: getAssetUrl(s3Assets.పలపటటImg), + text: "పాలపిట్ట", + audio: getAssetAudioUrl(s3Assets.పలపటటAudio), + }, + ], + correctWord: "బతుకమ్మ", + audio: getAssetAudioUrl(s3Assets.బతకమమAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బతకమమImg), + text: "బతుకమ్మ", + audio: getAssetAudioUrl(s3Assets.బతకమమAudio), + }, + { + img: getAssetUrl(s3Assets.వరణమలImg), + text: "వర్ణమాల", + audio: getAssetAudioUrl(s3Assets.వరణమలAudio), + }, + { + img: getAssetUrl(s3Assets.పలపటటImg), + text: "పాలపిట్ట", + audio: getAssetAudioUrl(s3Assets.పలపటటAudio), + }, + ], + correctWord: "పాలపిట్ట", + audio: getAssetAudioUrl(s3Assets.పలపటటAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వరణమలImg), + text: "వర్ణమాల", + audio: getAssetAudioUrl(s3Assets.వరణమలAudio), + }, + { + img: getAssetUrl(s3Assets.పటటకImg), + text: "పట్టిక", + audio: getAssetAudioUrl(s3Assets.పటటకAudio), + }, + { + img: getAssetUrl(s3Assets.గమమడImg), + text: "గుమ్మడి", + audio: getAssetAudioUrl(s3Assets.గమమడAudio), + }, + ], + correctWord: "వర్ణమాల", + audio: getAssetAudioUrl(s3Assets.వరణమలAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.గమమడImg), + text: "గుమ్మడి", + audio: getAssetAudioUrl(s3Assets.గమమడAudio), + }, + { + img: getAssetUrl(s3Assets.కయలడరImg), + text: "క్యాలెండర్", + audio: getAssetAudioUrl(s3Assets.కయలడరAudio), + }, + { + img: getAssetUrl(s3Assets.మదదబతImg), + text: "ముద్దబంతి", + audio: getAssetAudioUrl(s3Assets.మదదబతAudio), + }, + ], + correctWord: "ముద్దబంతి", + audio: getAssetAudioUrl(s3Assets.మదదబతAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పలపటటImg), + text: "పాలపిట్ట", + audio: getAssetAudioUrl(s3Assets.పలపటటAudio), + }, + { + img: getAssetUrl(s3Assets.చలకమమImg), + text: "చిలకమ్మ", + audio: getAssetAudioUrl(s3Assets.చలకమమAudio), + }, + { + img: getAssetUrl(s3Assets.వరషలImg), + text: "వర్షాలు", + audio: getAssetAudioUrl(s3Assets.వరషలAudio), + }, + ], + correctWord: "చిలకమ్మ", + audio: getAssetAudioUrl(s3Assets.చలకమమAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వరణమలImg), + text: "వర్ణమాల", + audio: getAssetAudioUrl(s3Assets.వరణమలAudio), + }, + { + img: getAssetUrl(s3Assets.కయలడరImg), + text: "క్యాలెండర్", + audio: getAssetAudioUrl(s3Assets.కయలడరAudio), + }, + { + img: getAssetUrl(s3Assets.బతకమమImg), + text: "బతుకమ్మ", + audio: getAssetAudioUrl(s3Assets.బతకమమAudio), + }, + ], + correctWord: "క్యాలెండర్", + audio: getAssetAudioUrl(s3Assets.కయలడరAudio), + flowName: "P4", + type: "soundMatch", + }, + ], + kn: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವನImg), + text: "ವನ", + audio: getAssetAudioUrl(s3Assets.ವನAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಜImg), + text: "ಮಜ", + audio: getAssetAudioUrl(s3Assets.ಮಜAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + } + ], + correctWord: "ವನ", + audio: getAssetAudioUrl(s3Assets.ವನAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ದನImg), + text: "ದನ", + audio: getAssetAudioUrl(s3Assets.ದನAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಜImg), + text: "ಮಜ", + audio: getAssetAudioUrl(s3Assets.ಮಜAudio), + }, + { + img: getAssetUrl(s3Assets.ರಥImg), + text: "ರಥ", + audio: getAssetAudioUrl(s3Assets.ರಥAudio), + } + ], + correctWord: "ರಥ", + audio: getAssetAudioUrl(s3Assets.ರಥAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವನImg), + text: "ವನ", + audio: getAssetAudioUrl(s3Assets.ವನAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + }, + { + img: getAssetUrl(s3Assets.ಪಟImg), + text: "ಪಟ", + audio: getAssetAudioUrl(s3Assets.ಪಟAudio), + } + ], + correctWord: "ಪಟ", + audio: getAssetAudioUrl(s3Assets.ಪಟAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ರಸImg), + text: "ರಸ", + audio: getAssetAudioUrl(s3Assets.ರಸAudio), + }, + { + img: getAssetUrl(s3Assets.ದನImg), + text: "ದನ", + audio: getAssetAudioUrl(s3Assets.ದನAudio), + }, + { + img: getAssetUrl(s3Assets.ವನImg), + text: "ವನ", + audio: getAssetAudioUrl(s3Assets.ವನAudio), + } + ], + correctWord: "ರಸ", + audio: getAssetAudioUrl(s3Assets.ರಸAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಜImg), + text: "ಮಜ", + audio: getAssetAudioUrl(s3Assets.ಮಜAudio), + }, + { + img: getAssetUrl(s3Assets.ಫಲಕImg), + text: "ಫಲಕ", + audio: getAssetAudioUrl(s3Assets.ಫಲಕAudio), + } + ], + correctWord: "ಮಜ", + audio: getAssetAudioUrl(s3Assets.ಮಜAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವನImg), + text: "ವನ", + audio: getAssetAudioUrl(s3Assets.ವನAudio), + }, + { + img: getAssetUrl(s3Assets.ರಥImg), + text: "ರಥ", + audio: getAssetAudioUrl(s3Assets.ರಥAudio), + }, + { + img: getAssetUrl(s3Assets.ದನImg), + text: "ದನ", + audio: getAssetAudioUrl(s3Assets.ದನAudio), + } + ], + correctWord: "ದನ", + audio: getAssetAudioUrl(s3Assets.ದನAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಶರImg), + text: "ಶರ", + audio: getAssetAudioUrl(s3Assets.ಶರAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + }, + { + img: getAssetUrl(s3Assets.ನಗರImg), + text: "ನಗರ", + audio: getAssetAudioUrl(s3Assets.ನಗರAudio), + } + ], + correctWord: "ಶರ", + audio: getAssetAudioUrl(s3Assets.ಶರAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವನImg), + text: "ವನ", + audio: getAssetAudioUrl(s3Assets.ವನAudio), + }, + { + img: getAssetUrl(s3Assets.ರಥImg), + text: "ರಥ", + audio: getAssetAudioUrl(s3Assets.ರಥAudio), + }, + { + img: getAssetUrl(s3Assets.ಫಲಕImg), + text: "ಫಲಕ", + audio: getAssetAudioUrl(s3Assets.ಫಲಕAudio), + } + ], + correctWord: "ಫಲಕ", + audio: getAssetAudioUrl(s3Assets.ಫಲಕAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಶರImg), + text: "ಶರ", + audio: getAssetAudioUrl(s3Assets.ಶರAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + }, + { + img: getAssetUrl(s3Assets.ನಗರImg), + text: "ನಗರ", + audio: getAssetAudioUrl(s3Assets.ನಗರAudio), + } + ], + correctWord: "ನಗರ", + audio: getAssetAudioUrl(s3Assets.ನಗರAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಶರImg), + text: "ಶರ", + audio: getAssetAudioUrl(s3Assets.ಶರAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + }, + { + img: getAssetUrl(s3Assets.ರಸImg), + text: "ರಸ", + audio: getAssetAudioUrl(s3Assets.ರಸAudio), + } + ], + correctWord: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + flowName: "P1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಶಲImg), + text: "ಶಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಶಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಗImg), + text: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + }, + { + img: getAssetUrl(s3Assets.ನರImg), + text: "ನೀರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + } + ], + correctWord: "ಶಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಶಲAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ತಟImg), + text: "ತುಟಿ", + audio: getAssetAudioUrl(s3Assets.ತಟAudio), + }, + { + img: getAssetUrl(s3Assets.ಭನImg), + text: "ಭಾನು", + audio: getAssetAudioUrl(s3Assets.ಭನAudio), + }, + { + img: getAssetUrl(s3Assets.ನಡImg), + text: "ನೋಡು", + audio: getAssetAudioUrl(s3Assets.ನಡAudio), + } + ], + correctWord: "ಭಾನು", + audio: getAssetAudioUrl(s3Assets.ಭನAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸದರImg), + text: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + }, + { + img: getAssetUrl(s3Assets.ನರImg), + text: "ನೀರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮರImg), + text: "ಮೂರು", + audio: getAssetAudioUrl(s3Assets.ಮರAudio), + } + ], + correctWord: "ಮೂರು", + audio: getAssetAudioUrl(s3Assets.ಮರAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಲImg), + text: "ಮಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಮಲAudio), + }, + { + img: getAssetUrl(s3Assets.ನರImg), + text: "ನೀರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + }, + { + img: getAssetUrl(s3Assets.ಎರಡImg), + text: "ಎರಡು", + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + } + ], + correctWord: "ನೀರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಗImg), + text: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + }, + { + img: getAssetUrl(s3Assets.ನಡImg), + text: "ನೋಡು", + audio: getAssetAudioUrl(s3Assets.ನಡAudio), + }, + { + img: getAssetUrl(s3Assets.ನರImg), + text: "ನೀರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + } + ], + correctWord: "ನೋಡು", + audio: getAssetAudioUrl(s3Assets.ನಡAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸದರImg), + text: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಗImg), + text: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + }, + { + img: getAssetUrl(s3Assets.ಮರImg), + text: "ಮೂರು", + audio: getAssetAudioUrl(s3Assets.ಮರAudio), + } + ], + correctWord: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಗImg), + text: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + }, + { + img: getAssetUrl(s3Assets.ನಡImg), + text: "ನೋಡು", + audio: getAssetAudioUrl(s3Assets.ನಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಲImg), + text: "ಮಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಮಲAudio), + } + ], + correctWord: "ಮಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಮಲAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ತಟImg), + text: "ತುಟಿ", + audio: getAssetAudioUrl(s3Assets.ತಟAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಲImg), + text: "ಮಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಮಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಶಲImg), + text: "ಶಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಶಲAudio), + } + ], + correctWord: "ತುಟಿ", + audio: getAssetAudioUrl(s3Assets.ತಟAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಶಲImg), + text: "ಶಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಶಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಸದರImg), + text: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಗImg), + text: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + } + ], + correctWord: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಎರಡImg), + text: "ಎರಡು", + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಭನImg), + text: "ಭಾನು", + audio: getAssetAudioUrl(s3Assets.ಭನAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಗImg), + text: "ಕಾಗೆ", + audio: getAssetAudioUrl(s3Assets.ಕಗAudio), + } + ], + correctWord: "ಎರಡು", + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + flowName: "P3", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಪಪImg), + text: "ಕಪ್ಪೆ", + audio: getAssetAudioUrl(s3Assets.ಕಪಪAudio), + }, + { + img: getAssetUrl(s3Assets.ಪರಕತImg), + text: "ಪ್ರಕೃತಿ", + audio: getAssetAudioUrl(s3Assets.ಪರಕತAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಲಲImg), + text: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + } + ], + correctWord: "ಕಪ್ಪೆ", + audio: getAssetAudioUrl(s3Assets.ಕಪಪAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಮಮರImg), + text: "ಕಮ್ಮಾರ", + audio: getAssetAudioUrl(s3Assets.ಕಮಮರAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಬಬImg), + text: "ಹಬ್ಬ", + audio: getAssetAudioUrl(s3Assets.ಹಬಬAudio), + }, + { + img: getAssetUrl(s3Assets.ಅವವImg), + text: "ಅವ್ವ", + audio: getAssetAudioUrl(s3Assets.ಅವವAudio), + } + ], + correctWord: "ಹಬ್ಬ", + audio: getAssetAudioUrl(s3Assets.ಹಬಬAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹಬಬImg), + text: "ಹಬ್ಬ", + audio: getAssetAudioUrl(s3Assets.ಹಬಬAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಮಮರImg), + text: "ಕಮ್ಮಾರ", + audio: getAssetAudioUrl(s3Assets.ಕಮಮರAudio), + }, + { + img: getAssetUrl(s3Assets.ಉಯಯಲImg), + text: "ಉಯ್ಯಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲAudio), + } + ], + correctWord: "ಕಮ್ಮಾರ", + audio: getAssetAudioUrl(s3Assets.ಕಮಮರAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಉಯಯಲImg), + text: "ಉಯ್ಯಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಅವವImg), + text: "ಅವ್ವ", + audio: getAssetAudioUrl(s3Assets.ಅವವAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಲಲImg), + text: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + } + ], + correctWord: "ಉಯ್ಯಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹಬಬImg), + text: "ಹಬ್ಬ", + audio: getAssetAudioUrl(s3Assets.ಹಬಬAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಪಪImg), + text: "ಕಪ್ಪೆ", + audio: getAssetAudioUrl(s3Assets.ಕಪಪAudio), + }, + { + img: getAssetUrl(s3Assets.ಚದರImg), + text: "ಚಂದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಚದರAudio), + } + ], + correctWord: "ಚಂದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಚದರAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹಲಲImg), + text: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಪಪImg), + text: "ಕಪ್ಪೆ", + audio: getAssetAudioUrl(s3Assets.ಕಪಪAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಬಬImg), + text: "ಹಬ್ಬ", + audio: getAssetAudioUrl(s3Assets.ಹಬಬAudio), + } + ], + correctWord: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಚದರImg), + text: "ಚಂದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಚದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಅವವImg), + text: "ಅವ್ವ", + audio: getAssetAudioUrl(s3Assets.ಅವವAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಸಸImg), + text: "ಬಸ್ಸು", + audio: getAssetAudioUrl(s3Assets.ಬಸಸAudio), + } + ], + correctWord: "ಅವ್ವ", + audio: getAssetAudioUrl(s3Assets.ಅವವAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅವವImg), + text: "ಅವ್ವ", + audio: getAssetAudioUrl(s3Assets.ಅವವAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಳಳImg), + text: "ಹಳ್ಳ", + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಸಸImg), + text: "ಬಸ್ಸು", + audio: getAssetAudioUrl(s3Assets.ಬಸಸAudio), + } + ], + correctWord: "ಬಸ್ಸು", + audio: getAssetAudioUrl(s3Assets.ಬಸಸAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಉಯಯಲImg), + text: "ಉಯ್ಯಾಲೆ", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಳಳImg), + text: "ಹಳ್ಳ", + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಚದರImg), + text: "ಚಂದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಚದರAudio), + } + ], + correctWord: "ಹಳ್ಳ", + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹಬಬImg), + text: "ಹಬ್ಬ", + audio: getAssetAudioUrl(s3Assets.ಹಬಬAudio), + }, + { + img: getAssetUrl(s3Assets.ಪರಕತImg), + text: "ಪ್ರಕೃತಿ", + audio: getAssetAudioUrl(s3Assets.ಪರಕತAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಳಳImg), + text: "ಹಳ್ಳ", + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + } + ], + correctWord: "ಪ್ರಕೃತಿ", + audio: getAssetAudioUrl(s3Assets.ಪರಕತAudio), + flowName: "P2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವಯಯಮImg), + text: "ವ್ಯಾಯಾಮ", + audio: getAssetAudioUrl(s3Assets.ವಯಯಮAudio), + }, + { + img: getAssetUrl(s3Assets.ವಳಯದಲImg), + text: "ವೀಳ್ಯೆದೆಲೆ", + audio: getAssetAudioUrl(s3Assets.ವಳಯದಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಸರಯImg), + text: "ಸೂರ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಸರಯAudio), + } + ], + correctWord: "ಸೂರ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಸರಯAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅಕಷರImg), + text: "ಅಕ್ಷರ", + audio: getAssetAudioUrl(s3Assets.ಅಕಷರAudio), + }, + { + img: getAssetUrl(s3Assets.ವಯಯಮImg), + text: "ವ್ಯಾಯಾಮ", + audio: getAssetAudioUrl(s3Assets.ವಯಯಮAudio), + }, + { + img: getAssetUrl(s3Assets.ವಜಞನImg), + text: "ವಿಜ್ಞಾನ", + audio: getAssetAudioUrl(s3Assets.ವಜಞನAudio), + } + ], + correctWord: "ಅಕ್ಷರ", + audio: getAssetAudioUrl(s3Assets.ಅಕಷರAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅಕಷರImg), + text: "ಅಕ್ಷರ", + audio: getAssetAudioUrl(s3Assets.ಅಕಷರAudio), + }, + { + img: getAssetUrl(s3Assets.ಉತಖನನImg), + text: "ಉತ್ಖನನ", + audio: getAssetAudioUrl(s3Assets.ಉತಖನನAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಣಕಟಟImg), + text: "ಅಣೆಕಟ್ಟು", + audio: getAssetAudioUrl(s3Assets.ಅಣಕಟಟAudio), + } + ], + correctWord: "ಉತ್ಖನನ", + audio: getAssetAudioUrl(s3Assets.ಉತಖನನAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವಜಞನImg), + text: "ವಿಜ್ಞಾನ", + audio: getAssetAudioUrl(s3Assets.ವಜಞನAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಷಕರImg), + text: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + }, + { + img: getAssetUrl(s3Assets.ನಮಸಕರImg), + text: "ನಮಸ್ಕಾರ", + audio: getAssetAudioUrl(s3Assets.ನಮಸಕರAudio), + } + ], + correctWord: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವಯಯಮImg), + text: "ವ್ಯಾಯಾಮ", + audio: getAssetAudioUrl(s3Assets.ವಯಯಮAudio), + }, + { + img: getAssetUrl(s3Assets.ಸರಯImg), + text: "ಸೂರ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಸರಯAudio), + }, + { + img: getAssetUrl(s3Assets.ನಮಸಕರImg), + text: "ನಮಸ್ಕಾರ", + audio: getAssetAudioUrl(s3Assets.ನಮಸಕರAudio), + } + ], + correctWord: "ವ್ಯಾಯಾಮ", + audio: getAssetAudioUrl(s3Assets.ವಯಯಮAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕರನಟಕImg), + text: "ಕರ್ನಾಟಕ", + audio: getAssetAudioUrl(s3Assets.ಕರನಟಕAudio), + }, + { + img: getAssetUrl(s3Assets.ವಳಯದಲImg), + text: "ವೀಳ್ಯೆದೆಲೆ", + audio: getAssetAudioUrl(s3Assets.ವಳಯದಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಷಕರImg), + text: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + } + ], + correctWord: "ಕರ್ನಾಟಕ", + audio: getAssetAudioUrl(s3Assets.ಕರನಟಕAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅಕಷರImg), + text: "ಅಕ್ಷರ", + audio: getAssetAudioUrl(s3Assets.ಅಕಷರAudio), + }, + { + img: getAssetUrl(s3Assets.ಕರನಟಕImg), + text: "ಕರ್ನಾಟಕ", + audio: getAssetAudioUrl(s3Assets.ಕರನಟಕAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಣಕಟಟImg), + text: "ಅಣೆಕಟ್ಟು", + audio: getAssetAudioUrl(s3Assets.ಅಣಕಟಟAudio), + } + ], + correctWord: "ಅಣೆಕಟ್ಟು", + audio: getAssetAudioUrl(s3Assets.ಅಣಕಟಟAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ನಮಸಕರImg), + text: "ನಮಸ್ಕಾರ", + audio: getAssetAudioUrl(s3Assets.ನಮಸಕರAudio), + }, + { + img: getAssetUrl(s3Assets.ಕರನಟಕImg), + text: "ಕರ್ನಾಟಕ", + audio: getAssetAudioUrl(s3Assets.ಕರನಟಕAudio), + }, + { + img: getAssetUrl(s3Assets.ವಳಯದಲImg), + text: "ವೀಳ್ಯೆದೆಲೆ", + audio: getAssetAudioUrl(s3Assets.ವಳಯದಲAudio), + } + ], + correctWord: "ನಮಸ್ಕಾರ", + audio: getAssetAudioUrl(s3Assets.ನಮಸಕರAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕರನಟಕImg), + text: "ಕರ್ನಾಟಕ", + audio: getAssetAudioUrl(s3Assets.ಕರನಟಕAudio), + }, + { + img: getAssetUrl(s3Assets.ಸರಯImg), + text: "ಸೂರ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಸರಯAudio), + }, + { + img: getAssetUrl(s3Assets.ವಜಞನImg), + text: "ವಿಜ್ಞಾನ", + audio: getAssetAudioUrl(s3Assets.ವಜಞನAudio), + } + ], + correctWord: "ವಿಜ್ಞಾನ", + audio: getAssetAudioUrl(s3Assets.ವಜಞನAudio), + flowName: "P4", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ವಳಯದಲImg), + text: "ವೀಳ್ಯೆದೆಲೆ", + audio: getAssetAudioUrl(s3Assets.ವಳಯದಲAudio), + }, + { + img: getAssetUrl(s3Assets.ನಮಸಕರImg), + text: "ನಮಸ್ಕಾರ", + audio: getAssetAudioUrl(s3Assets.ನಮಸಕರAudio), + }, + { + img: getAssetUrl(s3Assets.ಸರಯImg), + text: "ಸೂರ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಸರಯAudio), + } + ], + correctWord: "ವೀಳ್ಯೆದೆಲೆ", + audio: getAssetAudioUrl(s3Assets.ವಳಯದಲAudio), + flowName: "P4", + type: "soundMatch", + }, + ], +}; + +const SoundHunt = ({ + setVoiceText, + setRecordedAudio, + setVoiceAnimate, + storyLine, + type, + handleNext, + background, + parentWords = "", + enableNext, + showTimer, + points, + steps, + currentStep, + contentId, + contentType, + level, + isDiscover, + progressData, + showProgress, + playTeacherAudio = () => {}, + callUpdateLearner, + disableScreen, + isShowCase, + handleBack, + setEnableNext, + loading, + setOpenMessageDialog, + audio, + currentImg, + rStep, + vocabCount, + wordCount, +}) => { + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [selectedWord, setSelectedWord] = useState(null); + const [showConfetti, setShowConfetti] = useState(false); + const [wrongWord, setWrongWord] = useState(null); + const [recording, setRecording] = useState("no"); + const navigate = useNavigate(); + const [isPlaying, setIsPlaying] = useState(false); + const [isAudioPlayedOnce, setIsAudioPlayedOnce] = useState(false); + const [scale, setScale] = useState(1); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); + + useEffect(() => { + const interval = setInterval(() => { + setScale((prev) => (prev === 1 ? 1.2 : 1)); + }, 500); + + return () => clearInterval(interval); + }, []); + + // Filter content based on milestone level, step title, and steps prop (from API/config) + // Content selection logic: + // 1. Get milestone level (M1 or M2) from level prop + // 2. Get current step title from progressData (e.g., "P1", "P2", "P3", "P6", "P7") + // 3. Map step to flowNames based on milestone: + // - M1: Steps P1, P2, P6, P7 → show flowName P1, P3 + // - M2: Steps P1, P2, P6, P7 → show flowName P2, P4 + // - Other steps: use step title as flowName (e.g., P3 → flowName P3) + // 4. Limit by steps (contentCount from config) + const filteredContent = useMemo(() => { + // Get milestone level (level prop is number like 1, 2, etc.) + const milestoneLevel = level ? `m${level}` : null; + const language = getLocalData("lang"); + + // Get current step title from progressData + const currentStepTitle = + progressData?.currentPracticeStep !== undefined + ? practiceSteps?.[progressData.currentPracticeStep]?.title + : null; + + // Determine which flowNames to show based on milestone and step + let validFlowNames = null; + + if ( + milestoneLevel === "m1" && + currentStepTitle && + ["P1", "P2", "P6", "P7"].includes(currentStepTitle) + ) { + // M1: Steps P1, P2, P6, P7 → show flowName P1, P3 + validFlowNames = ["P1", "P3"]; + } else if ( + milestoneLevel === "m2" && + currentStepTitle && + ["P1", "P2", "P6", "P7"].includes(currentStepTitle) + ) { + // M2: Steps P1, P2, P6, P7 → show flowName P2, P4 + validFlowNames = ["P2", "P4"]; + } else if (currentStepTitle) { + // For other steps, use step title as flowName (e.g., P3 → flowName P3, P4 → flowName P4) + validFlowNames = [currentStepTitle]; + } + + // Filter content by valid flowNames + let stepContent = content[language]; + if (validFlowNames && validFlowNames.length > 0) { + stepContent = content[language].filter((item) => + validFlowNames.includes(item.flowName) + ); + } + + // If no content found, fallback to all content + if (stepContent.length === 0) { + stepContent = content[language]; + } + + // Limit by steps (contentCount from config) if provided + if (steps && steps > 0) { + return stepContent.slice(0, steps); + } + + // Default: return all filtered content for the step + return stepContent; + }, [steps, progressData, level]); + + const handleWordClick = (word) => { + setSelectedWord(word); + const currentQuestion = filteredContent[currentQuestionIndex]; + + if (word === currentQuestion.correctWord) { + const audio = new Audio(correctSound); + audio.play(); + setShowConfetti(true); + setWrongWord(null); + setTimeout(() => { + setShowConfetti(false); + setSelectedWord(null); + // setCurrentQuestionIndex( + // (prevIndex) => (prevIndex + 1) % content.L1.length + // ); + setRecording("recording"); + }, 3000); + } else { + const audio = new Audio(wrongSound); + audio.play(); + setWrongWord(word); + setTimeout(() => setWrongWord(null), 2000); + } + }; + + const currentQuestion = filteredContent[currentQuestionIndex]; + + const flowNames = [...new Set(filteredContent.map((item) => item.flowName))]; + const activeFlow = + filteredContent[currentQuestionIndex]?.flowName || flowNames[0]; + + const correctImage = currentQuestion?.allwords?.find( + (word) => word.text === currentQuestion?.correctWord + )?.img; + + let currentAudio = null; + + const handlePlayAudio = () => { + if (currentAudio) { + currentAudio.pause(); + } + + currentAudio = new Audio(filteredContent[currentQuestionIndex].audio); + + currentAudio.play(); + setIsPlaying(true); + setIsAudioPlayedOnce(true); + + currentAudio.onended = () => { + setIsPlaying(false); + }; + }; + + return ( + + {currentQuestion?.allwords ? ( +
+ {recording === "no" && ( + <> + {showConfetti && } + +
+ {[ + { top: "10%", left: "5%" }, + { top: "25%", left: "30%" }, + { top: "10%", left: "55%" }, + { top: "25%", left: "80%" }, + ].map((pos, index) => ( + {`Cloud + ))} +
+ + {selectedWord === currentQuestion?.correctWord ? ( +
+ Tick +
+ ) : wrongWord ? ( +
+ Wrong +
+ ) : ( + + )} + +
+ {currentQuestion?.allwords.map((item, index) => { + const isCorrect = + selectedWord === currentQuestion?.correctWord && + item.text === selectedWord; + const isWrong = wrongWord === item.text; + return ( +
{ + if (isAudioPlayedOnce) { + handleWordClick(item.text); + } + }} + > + {item.text} +
+ ); + })} +
+ + )} + {recording === "recording" && ( +
+
handleWordClick(currentQuestion.correctWord)} + > + {currentQuestion.correctWord} +
+ { + setRecording("startRec"); + }} + src={Assets.pzMic} + alt="mic" + style={{ width: "70px", height: "70px", cursor: "pointer" }} + /> +
+ )} + {recording === "startRec" && ( +
+
handleWordClick(currentQuestion.correctWord)} + > + {currentQuestion.correctWord} +
+ + + + { + const audio = new Audio(correctSound); + audio.play(); + setRecording("no"); + setIsPlaying(false); + setIsAudioPlayedOnce(false); + await handleNext(); + if (currentQuestionIndex === filteredContent.length - 1) { + return; + } + // if (currentQuestionIndex === filteredContent.length - 1) { + // // If handleNext prop is provided (e.g., from Practice flow), use it to update progress + // if (handleNext && typeof handleNext === "function") { + // // Call handleNext(true) to indicate mechanism is complete and trigger progress update + // await handleNext(); + // return; + // } else { + // // Standalone mode - navigate to discover-start + // setLocalData("rFlow", false); + // setLocalData("mFail", false); + // setLocalData("rStep", 0); + // if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + // navigate("/"); + // } else { + // navigate("/discover-start"); + // } + // } + // } else { + setCurrentQuestionIndex((prevIndex) => prevIndex + 1); + // } + }} + src={Assets.pause} + alt="Stop" + style={{ width: "60px", height: "60px", cursor: "pointer" }} + /> +
+ )} +
+ ) : ( +
+

{currentQuestion?.correctWord}

+ {correctImage && ( + {currentQuestion?.correctWord} + )} +
+ {recording === "no" ? ( + setRecording("startRec")} + src={Assets.mic} + alt="Start Recording" + style={{ width: "70px", height: "70px", cursor: "pointer" }} + /> + ) : ( +
+ + { + const audio = new Audio(correctSound); + audio.play(); + setRecording("no"); + setIsPlaying(false); + if (currentQuestionIndex === filteredContent.length - 1) { + // If handleNext prop is provided (e.g., from Practice flow), use it to update progress + if (handleNext && typeof handleNext === "function") { + // Call handleNext(true) to indicate mechanism is complete and trigger progress update + await handleNext(); + return; + } else { + // Standalone mode - navigate to discover-start + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + } + } else { + setCurrentQuestionIndex((prevIndex) => prevIndex + 1); + } + }} + src={Assets.pause} + alt="Stop Recording" + style={{ width: "60px", height: "60px", cursor: "pointer" }} + /> +
+ )} +
+
+ )} +
+ ); +}; + +export default SoundHunt; diff --git a/src/RFlow/SoundHuntS1Combined.jsx b/src/RFlow/SoundHuntS1Combined.jsx new file mode 100644 index 00000000..67032442 --- /dev/null +++ b/src/RFlow/SoundHuntS1Combined.jsx @@ -0,0 +1,8214 @@ +import React, { useState, useEffect, useMemo } from "react"; +import Confetti from "react-confetti"; +import * as Assets from "../utils/imageAudioLinks"; +import * as s3Assets from "../utils/s3Links"; +import { getAssetUrl } from "../utils/s3Links"; +import { getAssetAudioUrl } from "../utils/s3Links"; +import { + ThemeProvider, + createTheme, + useMediaQuery, + Grid, + Box, +} from "@mui/material"; +import MainLayout from "../components/Layouts.jsx/MainLayout"; +import listenImg from "../assets/listen.png"; +import correctSound from "../assets/correct.wav"; +import wrongSound from "../assets/audio/wrong.wav"; +import RecordVoiceVisualizer from "../utils/RecordVoiceVisualizer"; +import { + practiceSteps, + getLocalData, + NextButtonRound, + RetryIcon, + setLocalData, +} from "../utils/constants"; +import { getFontFamily } from "../utils/fontUtils"; +import { useNavigate } from "react-router-dom"; +import { + updateLearnerProfile, + getSetResultPractice, + callEngagementPredictor, +} from "../services/learnerAi/learnerAiService"; +import { addLesson } from "../services/orchestration/orchestrationService"; + +const theme = createTheme(); + +// Word Hunt (Sound Match) - Listen to Sound and choose the right word +const soundMatchContent = { + en: { + 1: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.cookImg), + text: "cook", + audio: getAssetAudioUrl(s3Assets.cookAudio), + }, + { + img: getAssetUrl(s3Assets.godImg2), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio2), + }, + { + img: getAssetUrl(s3Assets.badImg), + text: "bad", + audio: getAssetAudioUrl(s3Assets.badAudio), + }, + ], + correctWord: "bad", + audio: getAssetAudioUrl(s3Assets.badAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.godImg2), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio2), + }, + { + img: getAssetUrl(s3Assets.momImg), + text: "mom", + audio: getAssetAudioUrl(s3Assets.momAudio), + }, + { + img: getAssetUrl(s3Assets.goatImg), + text: "goat", + audio: getAssetAudioUrl(s3Assets.goatAudio), + }, + ], + correctWord: "mom", + audio: getAssetAudioUrl(s3Assets.momAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.hopImg), + text: "hop", + audio: getAssetAudioUrl(s3Assets.hopAudio), + }, + { + img: getAssetUrl(s3Assets.fatImg), + text: "fat", + audio: getAssetAudioUrl(s3Assets.fatAudio), + }, + { + img: getAssetUrl(s3Assets.cookImg), + text: "cook", + audio: getAssetAudioUrl(s3Assets.cookAudio), + }, + ], + correctWord: "hop", + audio: getAssetAudioUrl(s3Assets.hopAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.fatImg), + text: "fat", + audio: getAssetAudioUrl(s3Assets.fatAudio), + }, + { + img: getAssetUrl(s3Assets.momImg), + text: "mom", + audio: getAssetAudioUrl(s3Assets.momAudio), + }, + { + img: getAssetUrl(s3Assets.sadImg), + text: "sad", + audio: getAssetAudioUrl(s3Assets.sadAudio), + }, + ], + correctWord: "fat", + audio: getAssetAudioUrl(s3Assets.fatAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.goatImg), + text: "goat", + audio: getAssetAudioUrl(s3Assets.goatAudio), + }, + { + img: getAssetUrl(s3Assets.nineImg), + text: "nine", + audio: getAssetAudioUrl(s3Assets.nineAudio), + }, + { + img: getAssetUrl(s3Assets.himImg), + text: "him", + audio: getAssetAudioUrl(s3Assets.himAudio), + }, + ], + correctWord: "him", + audio: getAssetAudioUrl(s3Assets.himAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.godImg2), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio2), + }, + { + img: getAssetUrl(s3Assets.fatImg), + text: "fat", + audio: getAssetAudioUrl(s3Assets.fatAudio), + }, + { + img: getAssetUrl(s3Assets.sadImg), + text: "sad", + audio: getAssetAudioUrl(s3Assets.sadAudio), + }, + ], + correctWord: "sad", + audio: getAssetAudioUrl(s3Assets.sadAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.godImg2), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio2), + }, + { + img: getAssetUrl(s3Assets.cookImg), + text: "cook", + audio: getAssetAudioUrl(s3Assets.cookAudio), + }, + { + img: getAssetUrl(s3Assets.goatImg), + text: "goat", + audio: getAssetAudioUrl(s3Assets.goatAudio), + }, + ], + correctWord: "cook", + audio: getAssetAudioUrl(s3Assets.cookAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.godImg2), + text: "god", + audio: getAssetAudioUrl(s3Assets.godAudio2), + }, + { + img: getAssetUrl(s3Assets.nineImg), + text: "nine", + audio: getAssetAudioUrl(s3Assets.nineAudio), + }, + { + img: getAssetUrl(s3Assets.fatImg), + text: "fat", + audio: getAssetAudioUrl(s3Assets.fatAudio), + }, + ], + correctWord: "god", + audio: getAssetAudioUrl(s3Assets.godAudio2), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.cookImg), + text: "cook", + audio: getAssetAudioUrl(s3Assets.cookAudio), + }, + { + img: getAssetUrl(s3Assets.nineImg), + text: "nine", + audio: getAssetAudioUrl(s3Assets.nineAudio), + }, + { + img: getAssetUrl(s3Assets.fatImg), + text: "fat", + audio: getAssetAudioUrl(s3Assets.fatAudio), + }, + ], + correctWord: "nine", + audio: getAssetAudioUrl(s3Assets.nineAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.goatImg), + text: "goat", + audio: getAssetAudioUrl(s3Assets.goatAudio), + }, + { + img: getAssetUrl(s3Assets.badImg), + text: "bad", + audio: getAssetAudioUrl(s3Assets.badAudio), + }, + { + img: getAssetUrl(s3Assets.cookImg), + text: "cook", + audio: getAssetAudioUrl(s3Assets.cookAudio), + }, + ], + correctWord: "goat", + audio: getAssetAudioUrl(s3Assets.goatAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.wideImg), + text: "wide", + audio: getAssetAudioUrl(s3Assets.wideAudio), + }, + { + img: getAssetUrl(s3Assets.noteImg), + text: "note", + audio: getAssetAudioUrl(s3Assets.noteAudio), + }, + { + img: getAssetUrl(s3Assets.buyImg), + text: "buy", + audio: getAssetAudioUrl(s3Assets.buyAudio), + }, + ], + correctWord: "buy", + audio: getAssetAudioUrl(s3Assets.buyAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.happy2Img), + text: "happy", + audio: getAssetAudioUrl(s3Assets.happy3Audio), + }, + { + img: getAssetUrl(s3Assets.wideImg), + text: "wide", + audio: getAssetAudioUrl(s3Assets.wideAudio), + }, + { + img: getAssetUrl(s3Assets.fineImg), + text: "fine", + audio: getAssetAudioUrl(s3Assets.fineAudio), + }, + ], + correctWord: "fine", + audio: getAssetAudioUrl(s3Assets.fineAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.kindImg), + text: "kind", + audio: getAssetAudioUrl(s3Assets.kindAudio), + }, + { + img: getAssetUrl(s3Assets.bodyImg), + text: "body", + audio: getAssetAudioUrl(s3Assets.bodyAudio), + }, + { + img: getAssetUrl(s3Assets.buyImg), + text: "buy", + audio: getAssetAudioUrl(s3Assets.buyAudio), + }, + ], + correctWord: "kind", + audio: getAssetAudioUrl(s3Assets.kindAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.fineImg), + text: "fine", + audio: getAssetAudioUrl(s3Assets.fineAudio), + }, + { + img: getAssetUrl(s3Assets.halfImg), + text: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + }, + { + img: getAssetUrl(s3Assets.noteImg), + text: "note", + audio: getAssetAudioUrl(s3Assets.noteAudio), + }, + ], + correctWord: "note", + audio: getAssetAudioUrl(s3Assets.noteAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.happy2Img), + text: "happy", + audio: getAssetAudioUrl(s3Assets.happy3Audio), + }, + { + img: getAssetUrl(s3Assets.wideImg), + text: "wide", + audio: getAssetAudioUrl(s3Assets.wideAudio), + }, + { + img: getAssetUrl(s3Assets.hideImg), + text: "hide", + audio: getAssetAudioUrl(s3Assets.hideAudio), + }, + ], + correctWord: "wide", + audio: getAssetAudioUrl(s3Assets.wideAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.halfImg), + text: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + }, + { + img: getAssetUrl(s3Assets.knowImg), + text: "know", + audio: getAssetAudioUrl(s3Assets.knowAudio), + }, + { + img: getAssetUrl(s3Assets.hideImg), + text: "hide", + audio: getAssetAudioUrl(s3Assets.hideAudio), + }, + ], + correctWord: "know", + audio: getAssetAudioUrl(s3Assets.knowAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.fineImg), + text: "fine", + audio: getAssetAudioUrl(s3Assets.fineAudio), + }, + { + img: getAssetUrl(s3Assets.halfImg), + text: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + }, + { + img: getAssetUrl(s3Assets.bodyImg), + text: "body", + audio: getAssetAudioUrl(s3Assets.bodyAudio), + }, + ], + correctWord: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.buyImg), + text: "buy", + audio: getAssetAudioUrl(s3Assets.buyAudio), + }, + { + img: getAssetUrl(s3Assets.halfImg), + text: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + }, + { + img: getAssetUrl(s3Assets.hideImg), + text: "hide", + audio: getAssetAudioUrl(s3Assets.hideAudio), + }, + ], + correctWord: "hide", + audio: getAssetAudioUrl(s3Assets.hideAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.knowImg), + text: "know", + audio: getAssetAudioUrl(s3Assets.knowAudio), + }, + { + img: getAssetUrl(s3Assets.happy2Img), + text: "happy", + audio: getAssetAudioUrl(s3Assets.happy3Audio), + }, + { + img: getAssetUrl(s3Assets.halfImg), + text: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + }, + ], + correctWord: "happy", + audio: getAssetAudioUrl(s3Assets.happy3Audio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.halfImg), + text: "half", + audio: getAssetAudioUrl(s3Assets.halfAudio), + }, + { + img: getAssetUrl(s3Assets.bodyImg), + text: "body", + audio: getAssetAudioUrl(s3Assets.bodyAudio), + }, + { + img: getAssetUrl(s3Assets.knowImg), + text: "know", + audio: getAssetAudioUrl(s3Assets.knowAudio), + }, + ], + correctWord: "body", + audio: getAssetAudioUrl(s3Assets.bodyAudio), + flowName: "S2", + type: "soundMatch", + }, + ], + + 2: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.sonImg), + text: "Son", + audio: getAssetAudioUrl(s3Assets.sonAudio), + }, + { + img: getAssetUrl(s3Assets.chairImg), + text: "chair", + audio: getAssetAudioUrl(s3Assets.chairAudio), + }, + { + img: getAssetUrl(s3Assets.fairImg), + text: "fair", + audio: getAssetAudioUrl(s3Assets.fairAudio), + }, + ], + correctWord: "Son", + audio: getAssetAudioUrl(s3Assets.sonAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.zigImg), + text: "zig", + audio: getAssetAudioUrl(s3Assets.zigAudio), + }, + { + img: getAssetUrl(s3Assets.fairImg), + text: "fair", + audio: getAssetAudioUrl(s3Assets.fairAudio), + }, + { + img: getAssetUrl(s3Assets.chatImg), + text: "chat", + audio: getAssetAudioUrl(s3Assets.chatAudio), + }, + ], + correctWord: "zig", + audio: getAssetAudioUrl(s3Assets.zigAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.logImg), + text: "log", + audio: getAssetAudioUrl(s3Assets.logAudio), + }, + { + img: getAssetUrl(s3Assets.sonImg), + text: "Son", + audio: getAssetAudioUrl(s3Assets.sonAudio), + }, + { + img: getAssetUrl(s3Assets.birdImg2), + text: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio2), + }, + ], + correctWord: "log", + audio: getAssetAudioUrl(s3Assets.logAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.penImg), + text: "pen", + audio: getAssetAudioUrl(s3Assets.penAudio), + }, + { + img: getAssetUrl(s3Assets.nowImg), + text: "now", + audio: getAssetAudioUrl(s3Assets.nowAudio), + }, + { + img: getAssetUrl(s3Assets.fairImg), + text: "fair", + audio: getAssetAudioUrl(s3Assets.fairAudio), + }, + ], + correctWord: "now", + audio: getAssetAudioUrl(s3Assets.nowAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.chatImg), + text: "chat", + audio: getAssetAudioUrl(s3Assets.chatAudio), + }, + { + img: getAssetUrl(s3Assets.sonImg), + text: "Son", + audio: getAssetAudioUrl(s3Assets.sonAudio), + }, + { + img: getAssetUrl(s3Assets.nowImg), + text: "now", + audio: getAssetAudioUrl(s3Assets.nowAudio), + }, + ], + correctWord: "chat", + audio: getAssetAudioUrl(s3Assets.chatAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.logImg), + text: "log", + audio: getAssetAudioUrl(s3Assets.logAudio), + }, + { + img: getAssetUrl(s3Assets.chatImg), + text: "chat", + audio: getAssetAudioUrl(s3Assets.chatAudio), + }, + { + img: getAssetUrl(s3Assets.penImg), + text: "pen", + audio: getAssetAudioUrl(s3Assets.penAudio), + }, + ], + correctWord: "pen", + audio: getAssetAudioUrl(s3Assets.penAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.nowImg), + text: "now", + audio: getAssetAudioUrl(s3Assets.nowAudio), + }, + { + img: getAssetUrl(s3Assets.birdImg2), + text: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio2), + }, + { + img: getAssetUrl(s3Assets.fairImg), + text: "fair", + audio: getAssetAudioUrl(s3Assets.fairAudio), + }, + ], + correctWord: "fair", + audio: getAssetAudioUrl(s3Assets.fairAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.birdImg2), + text: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio2), + }, + { + img: getAssetUrl(s3Assets.careImg), + text: "care", + audio: getAssetAudioUrl(s3Assets.careAudio), + }, + { + img: getAssetUrl(s3Assets.zigImg), + text: "zig", + audio: getAssetAudioUrl(s3Assets.zigAudio), + }, + ], + correctWord: "care", + audio: getAssetAudioUrl(s3Assets.careAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.zigImg), + text: "zig", + audio: getAssetAudioUrl(s3Assets.zigAudio), + }, + { + img: getAssetUrl(s3Assets.chatImg), + text: "chat", + audio: getAssetAudioUrl(s3Assets.chatAudio), + }, + { + img: getAssetUrl(s3Assets.birdImg2), + text: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio2), + }, + ], + correctWord: "bird", + audio: getAssetAudioUrl(s3Assets.birdAudio2), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.careImg), + text: "care", + audio: getAssetAudioUrl(s3Assets.careAudio), + }, + { + img: getAssetUrl(s3Assets.logImg), + text: "log", + audio: getAssetAudioUrl(s3Assets.logAudio), + }, + { + img: getAssetUrl(s3Assets.chairImg), + text: "chair", + audio: getAssetAudioUrl(s3Assets.chairAudio), + }, + ], + correctWord: "chair", + audio: getAssetAudioUrl(s3Assets.chairAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.orangeImg), + text: "orange", + audio: getAssetAudioUrl(s3Assets.orangeAudio), + }, + { + img: getAssetUrl(s3Assets.turnImg), + text: "turn", + audio: getAssetAudioUrl(s3Assets.turnAudio), + }, + { + img: getAssetUrl(s3Assets.dearImg), + text: "dear", + audio: getAssetAudioUrl(s3Assets.dearAudio), + }, + ], + correctWord: "turn", + audio: getAssetAudioUrl(s3Assets.turnAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.earthImg2), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio2), + }, + { + img: getAssetUrl(s3Assets.sootheImg), + text: "soothe", + audio: getAssetAudioUrl(s3Assets.sootheAudio), + }, + { + img: getAssetUrl(s3Assets.dearImg), + text: "dear", + audio: getAssetAudioUrl(s3Assets.dearAudio), + }, + ], + correctWord: "soothe", + audio: getAssetAudioUrl(s3Assets.sootheAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.lazyImg), + text: "lazy", + audio: getAssetAudioUrl(s3Assets.lazyAudio), + }, + { + img: getAssetUrl(s3Assets.perkImg), + text: "perk", + audio: getAssetAudioUrl(s3Assets.perkAudio), + }, + { + img: getAssetUrl(s3Assets.earImg), + text: "ear", + audio: getAssetAudioUrl(s3Assets.earAudio), + }, + ], + correctWord: "perk", + audio: getAssetAudioUrl(s3Assets.perkAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.earthImg2), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio2), + }, + { + img: getAssetUrl(s3Assets.royalImg), + text: "royal", + audio: getAssetAudioUrl(s3Assets.royalAudio), + }, + { + img: getAssetUrl(s3Assets.dearImg), + text: "dear", + audio: getAssetAudioUrl(s3Assets.dearAudio), + }, + ], + correctWord: "dear", + audio: getAssetAudioUrl(s3Assets.dearAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.perkImg), + text: "perk", + audio: getAssetAudioUrl(s3Assets.perkAudio), + }, + { + img: getAssetUrl(s3Assets.purpleImg2), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + }, + { + img: getAssetUrl(s3Assets.royalImg), + text: "royal", + audio: getAssetAudioUrl(s3Assets.royalAudio), + }, + ], + correctWord: "royal", + audio: getAssetAudioUrl(s3Assets.royalAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.dearImg), + text: "dear", + audio: getAssetAudioUrl(s3Assets.dearAudio), + }, + { + img: getAssetUrl(s3Assets.earImg), + text: "ear", + audio: getAssetAudioUrl(s3Assets.earAudio), + }, + { + img: getAssetUrl(s3Assets.royalImg), + text: "royal", + audio: getAssetAudioUrl(s3Assets.royalAudio), + }, + ], + correctWord: "ear", + audio: getAssetAudioUrl(s3Assets.earAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.lazyImg), + text: "lazy", + audio: getAssetAudioUrl(s3Assets.lazyAudio), + }, + { + img: getAssetUrl(s3Assets.earthImg2), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio2), + }, + { + img: getAssetUrl(s3Assets.purpleImg2), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + }, + ], + correctWord: "lazy", + audio: getAssetAudioUrl(s3Assets.lazyAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.sootheImg), + text: "soothe", + audio: getAssetAudioUrl(s3Assets.sootheAudio), + }, + { + img: getAssetUrl(s3Assets.perkImg), + text: "perk", + audio: getAssetAudioUrl(s3Assets.perkAudio), + }, + { + img: getAssetUrl(s3Assets.orangeImg), + text: "orange", + audio: getAssetAudioUrl(s3Assets.orangeAudio), + }, + ], + correctWord: "orange", + audio: getAssetAudioUrl(s3Assets.orangeAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.earImg), + text: "ear", + audio: getAssetAudioUrl(s3Assets.earAudio), + }, + { + img: getAssetUrl(s3Assets.perkImg), + text: "perk", + audio: getAssetAudioUrl(s3Assets.perkAudio), + }, + { + img: getAssetUrl(s3Assets.purpleImg2), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + }, + ], + correctWord: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.purpleImg2), + text: "purple", + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + }, + { + img: getAssetUrl(s3Assets.earthImg2), + text: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio2), + }, + { + img: getAssetUrl(s3Assets.lazyImg), + text: "lazy", + audio: getAssetAudioUrl(s3Assets.lazyAudio), + }, + ], + correctWord: "earth", + audio: getAssetAudioUrl(s3Assets.earthAudio2), + flowName: "S2", + type: "soundMatch", + }, + ], + }, + te: { + 1: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.జకImg), + text: "జింక", + audio: getAssetAudioUrl(s3Assets.జకAudio), + }, + { + img: getAssetUrl(s3Assets.ఝషImg), + text: "ఝషం", + audio: getAssetAudioUrl(s3Assets.ఝషAudio), + }, + { + img: getAssetUrl(s3Assets.కజరImg), + text: "కంజర", + audio: getAssetAudioUrl(s3Assets.కజరAudio), + }, + ], + correctWord: "ఝషం", + audio: getAssetAudioUrl(s3Assets.ఝషAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.తలగడImg), + text: "తలగడ", + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + }, + { + img: getAssetUrl(s3Assets.మడImg), + text: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio), + }, + { + img: getAssetUrl(s3Assets.గటImg), + text: "గంట", + audio: getAssetAudioUrl(s3Assets.గటAudio), + }, + ], + correctWord: "గంట", + audio: getAssetAudioUrl(s3Assets.గటAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.జమImg), + text: "జామ", + audio: getAssetAudioUrl(s3Assets.జమAudio), + }, + { + img: getAssetUrl(s3Assets.పడవImg), + text: "పడవ", + audio: getAssetAudioUrl(s3Assets.పడవAudio), + }, + { + img: getAssetUrl(s3Assets.తలగడImg), + text: "తలగడ", + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + }, + ], + correctWord: "పడవ", + audio: getAssetAudioUrl(s3Assets.పడవAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.గటImg), + text: "గంట", + audio: getAssetAudioUrl(s3Assets.గటAudio), + }, + { + img: getAssetUrl(s3Assets.తలగడImg), + text: "తలగడ", + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + }, + { + img: getAssetUrl(s3Assets.దడImg), + text: "దండం", + audio: getAssetAudioUrl(s3Assets.దడAudio), + }, + ], + correctWord: "తలగడ", + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.జమImg), + text: "జామ", + audio: getAssetAudioUrl(s3Assets.జమAudio), + }, + { + img: getAssetUrl(s3Assets.దడImg), + text: "దండం", + audio: getAssetAudioUrl(s3Assets.దడAudio), + }, + { + img: getAssetUrl(s3Assets.పడవImg), + text: "పడవ", + audio: getAssetAudioUrl(s3Assets.పడవAudio), + }, + ], + correctWord: "జామ", + audio: getAssetAudioUrl(s3Assets.జమAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కజరImg), + text: "కంజర", + audio: getAssetAudioUrl(s3Assets.కజరAudio), + }, + { + img: getAssetUrl(s3Assets.జలImg), + text: "జలం", + audio: getAssetAudioUrl(s3Assets.జలAudio), + }, + { + img: getAssetUrl(s3Assets.పడవImg), + text: "పడవ", + audio: getAssetAudioUrl(s3Assets.పడవAudio), + }, + ], + correctWord: "జలం", + audio: getAssetAudioUrl(s3Assets.జలAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.జకImg), + text: "జింక", + audio: getAssetAudioUrl(s3Assets.జకAudio), + }, + { + img: getAssetUrl(s3Assets.గటImg), + text: "గంట", + audio: getAssetAudioUrl(s3Assets.గటAudio), + }, + { + img: getAssetUrl(s3Assets.పడవImg), + text: "పడవ", + audio: getAssetAudioUrl(s3Assets.పడవAudio), + }, + ], + correctWord: "జింక", + audio: getAssetAudioUrl(s3Assets.జకAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.దడImg), + text: "దండం", + audio: getAssetAudioUrl(s3Assets.దడAudio), + }, + { + img: getAssetUrl(s3Assets.జమImg), + text: "జామ", + audio: getAssetAudioUrl(s3Assets.జమAudio), + }, + { + img: getAssetUrl(s3Assets.జలImg), + text: "జలం", + audio: getAssetAudioUrl(s3Assets.జలAudio), + }, + ], + correctWord: "దండం", + audio: getAssetAudioUrl(s3Assets.దడAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.మడImg), + text: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio), + }, + { + img: getAssetUrl(s3Assets.దడImg), + text: "దండం", + audio: getAssetAudioUrl(s3Assets.దడAudio), + }, + { + img: getAssetUrl(s3Assets.కజరImg), + text: "కంజర", + audio: getAssetAudioUrl(s3Assets.కజరAudio), + }, + ], + correctWord: "కంజర", + audio: getAssetAudioUrl(s3Assets.కజరAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పడవImg), + text: "పడవ", + audio: getAssetAudioUrl(s3Assets.పడవAudio), + }, + { + img: getAssetUrl(s3Assets.మడImg), + text: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio), + }, + { + img: getAssetUrl(s3Assets.జలImg), + text: "జలం", + audio: getAssetAudioUrl(s3Assets.జలAudio), + }, + ], + correctWord: "మూడు", + audio: getAssetAudioUrl(s3Assets.మడAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఏనగImg), + text: "ఏనుగు", + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + }, + { + img: getAssetUrl(s3Assets.పజరImg), + text: "పంజరం", + audio: getAssetAudioUrl(s3Assets.పజర2Audio), + }, + { + img: getAssetUrl(s3Assets.కడImg), + text: "కోడి", + audio: getAssetAudioUrl(s3Assets.కడAudio), + }, + ], + correctWord: "కోడి", + audio: getAssetAudioUrl(s3Assets.కడAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.తళImg), + text: "తాళం", + audio: getAssetAudioUrl(s3Assets.తళAudio), + }, + { + img: getAssetUrl(s3Assets.చయImg), + text: "చేయి", + audio: getAssetAudioUrl(s3Assets.చయAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + ], + correctWord: "తాళం", + audio: getAssetAudioUrl(s3Assets.తళAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చయImg), + text: "చేయి", + audio: getAssetAudioUrl(s3Assets.చయAudio), + }, + { + img: getAssetUrl(s3Assets.కలశImg), + text: "కలశం", + audio: getAssetAudioUrl(s3Assets.కలశAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + ], + correctWord: "చేయి", + audio: getAssetAudioUrl(s3Assets.చయAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + { + img: getAssetUrl(s3Assets.చయImg), + text: "చేయి", + audio: getAssetAudioUrl(s3Assets.చయAudio), + }, + { + img: getAssetUrl(s3Assets.ఉడతImg), + text: "ఉడుత", + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + }, + ], + correctWord: "ఉడుత", + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కతలImg), + text: "కోతులు", + audio: getAssetAudioUrl(s3Assets.కతలAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + { + img: getAssetUrl(s3Assets.ఏనగImg), + text: "ఏనుగు", + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + }, + ], + correctWord: "కోతులు", + audio: getAssetAudioUrl(s3Assets.కతలAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.సనమImg), + text: "సినిమా", + audio: getAssetAudioUrl(s3Assets.సనమAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + { + img: getAssetUrl(s3Assets.కలశImg), + text: "కలశం", + audio: getAssetAudioUrl(s3Assets.కలశAudio), + }, + ], + correctWord: "సినిమా", + audio: getAssetAudioUrl(s3Assets.సనమAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + { + img: getAssetUrl(s3Assets.కలశImg), + text: "కలశం", + audio: getAssetAudioUrl(s3Assets.కలశAudio), + }, + { + img: getAssetUrl(s3Assets.కతలImg), + text: "కోతులు", + audio: getAssetAudioUrl(s3Assets.కతలAudio), + }, + ], + correctWord: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కలశImg), + text: "కలశం", + audio: getAssetAudioUrl(s3Assets.కలశAudio), + }, + { + img: getAssetUrl(s3Assets.నరImg2), + text: "నూరు", + audio: getAssetAudioUrl(s3Assets.నరAudio2), + }, + { + img: getAssetUrl(s3Assets.కతలImg), + text: "కోతులు", + audio: getAssetAudioUrl(s3Assets.కతలAudio), + }, + ], + correctWord: "కలశం", + audio: getAssetAudioUrl(s3Assets.కలశAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కతలImg), + text: "కోతులు", + audio: getAssetAudioUrl(s3Assets.కతలAudio), + }, + { + img: getAssetUrl(s3Assets.ఏనగImg), + text: "ఏనుగు", + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + }, + { + img: getAssetUrl(s3Assets.ఉడతImg), + text: "ఉడుత", + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + }, + ], + correctWord: "ఏనుగు", + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఉడతImg), + text: "ఉడుత", + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + }, + { + img: getAssetUrl(s3Assets.పజరImg), + text: "పంజరం", + audio: getAssetAudioUrl(s3Assets.పజర2Audio), + }, + { + img: getAssetUrl(s3Assets.ఏనగImg), + text: "ఏనుగు", + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + }, + ], + correctWord: "పంజరం", + audio: getAssetAudioUrl(s3Assets.పజర2Audio), + flowName: "S2", + type: "soundMatch", + }, + ], + 2: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.చకకImg), + text: "చెక్క", + audio: getAssetAudioUrl(s3Assets.చకకAudio), + }, + { + img: getAssetUrl(s3Assets.వమనImg), + text: "విమానం", + audio: getAssetAudioUrl(s3Assets.వమనAudio), + }, + { + img: getAssetUrl(s3Assets.కరటImg), + text: "కిరీటం", + audio: getAssetAudioUrl(s3Assets.కరటAudio), + }, + ], + correctWord: "కిరీటం", + audio: getAssetAudioUrl(s3Assets.కరటAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పజరImg), + text: "పూజారి", + audio: getAssetAudioUrl(s3Assets.పజరAudio), + }, + { + img: getAssetUrl(s3Assets.చకకImg), + text: "చెక్క", + audio: getAssetAudioUrl(s3Assets.చకకAudio), + }, + { + img: getAssetUrl(s3Assets.మకడImg), + text: "మూకుడు", + audio: getAssetAudioUrl(s3Assets.మకడAudio), + }, + ], + correctWord: "మూకుడు", + audio: getAssetAudioUrl(s3Assets.మకడAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.మసలImg), + text: "మొసలి", + audio: getAssetAudioUrl(s3Assets.మసలAudio), + }, + { + img: getAssetUrl(s3Assets.అభనయImg), + text: "అభినయం", + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + }, + { + img: getAssetUrl(s3Assets.వమనImg), + text: "విమానం", + audio: getAssetAudioUrl(s3Assets.వమనAudio), + }, + ], + correctWord: "అభినయం", + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పజరImg), + text: "పూజారి", + audio: getAssetAudioUrl(s3Assets.పజరAudio), + }, + { + img: getAssetUrl(s3Assets.తపపImg), + text: "తప్పు", + audio: getAssetAudioUrl(s3Assets.తపపAudio), + }, + { + img: getAssetUrl(s3Assets.కరటImg), + text: "కిరీటం", + audio: getAssetAudioUrl(s3Assets.కరటAudio), + }, + ], + correctWord: "తప్పు", + audio: getAssetAudioUrl(s3Assets.తపపAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బడకయImg), + text: "బెండకాయ", + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + }, + { + img: getAssetUrl(s3Assets.చకకImg), + text: "చెక్క", + audio: getAssetAudioUrl(s3Assets.చకకAudio), + }, + { + img: getAssetUrl(s3Assets.మకడImg), + text: "మూకుడు", + audio: getAssetAudioUrl(s3Assets.మకడAudio), + }, + ], + correctWord: "చెక్క", + audio: getAssetAudioUrl(s3Assets.చకకAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.మసలImg), + text: "మొసలి", + audio: getAssetAudioUrl(s3Assets.మసలAudio), + }, + { + img: getAssetUrl(s3Assets.పజరImg), + text: "పూజారి", + audio: getAssetAudioUrl(s3Assets.పజరAudio), + }, + { + img: getAssetUrl(s3Assets.చకకImg), + text: "చెక్క", + audio: getAssetAudioUrl(s3Assets.చకకAudio), + }, + ], + correctWord: "మొసలి", + audio: getAssetAudioUrl(s3Assets.మసలAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బమమImg), + text: "బొమ్మ", + audio: getAssetAudioUrl(s3Assets.బమమAudio), + }, + { + img: getAssetUrl(s3Assets.అభనయImg), + text: "అభినయం", + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + }, + { + img: getAssetUrl(s3Assets.బడకయImg), + text: "బెండకాయ", + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + }, + ], + correctWord: "బెండకాయ", + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వమనImg), + text: "విమానం", + audio: getAssetAudioUrl(s3Assets.వమనAudio), + }, + { + img: getAssetUrl(s3Assets.తపపImg), + text: "తప్పు", + audio: getAssetAudioUrl(s3Assets.తపపAudio), + }, + { + img: getAssetUrl(s3Assets.మసలImg), + text: "మొసలి", + audio: getAssetAudioUrl(s3Assets.మసలAudio), + }, + ], + correctWord: "విమానం", + audio: getAssetAudioUrl(s3Assets.వమనAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చకకImg), + text: "చెక్క", + audio: getAssetAudioUrl(s3Assets.చకకAudio), + }, + { + img: getAssetUrl(s3Assets.బమమImg), + text: "బొమ్మ", + audio: getAssetAudioUrl(s3Assets.బమమAudio), + }, + { + img: getAssetUrl(s3Assets.మసలImg), + text: "మొసలి", + audio: getAssetAudioUrl(s3Assets.మసలAudio), + }, + ], + correctWord: "బొమ్మ", + audio: getAssetAudioUrl(s3Assets.బమమAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.అభనయImg), + text: "అభినయం", + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + }, + { + img: getAssetUrl(s3Assets.పజరImg), + text: "పూజారి", + audio: getAssetAudioUrl(s3Assets.పజరAudio), + }, + { + img: getAssetUrl(s3Assets.బడకయImg), + text: "బెండకాయ", + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + }, + ], + correctWord: "పూజారి", + audio: getAssetAudioUrl(s3Assets.పజరAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వదయలImg), + text: "వాద్యాల", + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + }, + { + img: getAssetUrl(s3Assets.దరవజImg), + text: "దర్వాజ", + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + }, + { + img: getAssetUrl(s3Assets.వకషImg), + text: "వృక్షం", + audio: getAssetAudioUrl(s3Assets.వకషAudio), + }, + ], + correctWord: "దర్వాజ", + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వదయలImg), + text: "వాద్యాల", + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + }, + { + img: getAssetUrl(s3Assets.దరవజImg), + text: "దర్వాజ", + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + }, + { + img: getAssetUrl(s3Assets.వననలImg), + text: "వెన్నెల", + audio: getAssetAudioUrl(s3Assets.వననలAudio), + }, + ], + correctWord: "వాద్యాల", + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బరరకథImg), + text: "బుర్రకథ", + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + }, + { + img: getAssetUrl(s3Assets.వదయలImg), + text: "వాద్యాల", + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + }, + { + img: getAssetUrl(s3Assets.వకషImg), + text: "వృక్షం", + audio: getAssetAudioUrl(s3Assets.వకషAudio), + }, + ], + correctWord: "వృక్షం", + audio: getAssetAudioUrl(s3Assets.వకషAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పరపచImg), + text: "ప్రపంచం", + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + }, + { + img: getAssetUrl(s3Assets.ఇలలImg), + text: "ఇల్లు", + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + }, + { + img: getAssetUrl(s3Assets.బరరకథImg), + text: "బుర్రకథ", + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + }, + ], + correctWord: "ప్రపంచం", + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చననImg), + text: "చిన్ని", + audio: getAssetAudioUrl(s3Assets.చననAudio), + }, + { + img: getAssetUrl(s3Assets.సరమలలImg), + text: "సిరిమల్లె", + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + }, + { + img: getAssetUrl(s3Assets.దరవజImg), + text: "దర్వాజ", + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + }, + ], + correctWord: "సిరిమల్లె", + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వదయలImg), + text: "వాద్యాల", + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + }, + { + img: getAssetUrl(s3Assets.బయయImg), + text: "బియ్యం", + audio: getAssetAudioUrl(s3Assets.బయయAudio), + }, + { + img: getAssetUrl(s3Assets.చననImg), + text: "చిన్ని", + audio: getAssetAudioUrl(s3Assets.చననAudio), + }, + ], + correctWord: "బియ్యం", + audio: getAssetAudioUrl(s3Assets.బయయAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఇలలImg), + text: "ఇల్లు", + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + }, + { + img: getAssetUrl(s3Assets.వననలImg), + text: "వెన్నెల", + audio: getAssetAudioUrl(s3Assets.వననలAudio), + }, + { + img: getAssetUrl(s3Assets.పరపచImg), + text: "ప్రపంచం", + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + }, + ], + correctWord: "ఇల్లు", + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.సరమలలImg), + text: "సిరిమల్లె", + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + }, + { + img: getAssetUrl(s3Assets.చననImg), + text: "చిన్ని", + audio: getAssetAudioUrl(s3Assets.చననAudio), + }, + { + img: getAssetUrl(s3Assets.ఇలలImg), + text: "ఇల్లు", + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + }, + ], + correctWord: "చిన్ని", + audio: getAssetAudioUrl(s3Assets.చననAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చననImg), + text: "చిన్ని", + audio: getAssetAudioUrl(s3Assets.చననAudio), + }, + { + img: getAssetUrl(s3Assets.ఇలలImg), + text: "ఇల్లు", + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + }, + { + img: getAssetUrl(s3Assets.బరరకథImg), + text: "బుర్రకథ", + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + }, + ], + correctWord: "బుర్రకథ", + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పరపచImg), + text: "ప్రపంచం", + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + }, + { + img: getAssetUrl(s3Assets.సరమలలImg), + text: "సిరిమల్లె", + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + }, + { + img: getAssetUrl(s3Assets.వననలImg), + text: "వెన్నెల", + audio: getAssetAudioUrl(s3Assets.వననలAudio), + }, + ], + correctWord: "వెన్నెల", + audio: getAssetAudioUrl(s3Assets.వననలAudio), + flowName: "S2", + type: "soundMatch", + }, + ], + 3: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.అమలపలకImg), + text: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + }, + { + img: getAssetUrl(s3Assets.వపకచదImg), + text: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + }, + { + img: getAssetUrl(s3Assets.గననలపలImg), + text: "గిన్నెలో పాలు", + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + }, + ], + correctWord: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio2), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.అమలపలకImg), + text: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + }, + { + img: getAssetUrl(s3Assets.బవగరకImg), + text: "బావి గిరక", + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + }, + { + img: getAssetUrl(s3Assets.పసరగరలImg), + text: "పెసర గారెలు", + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + }, + ], + correctWord: "బావి గిరక", + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బవగరకImg), + text: "బావి గిరక", + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + }, + { + img: getAssetUrl(s3Assets.అమలపలకImg), + text: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + }, + { + img: getAssetUrl(s3Assets.పసరగరలImg), + text: "పెసర గారెలు", + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + }, + ], + correctWord: "పెసర గారెలు", + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఎరరగలబImg), + text: "ఎర్ర గులాబి", + audio: getAssetAudioUrl(s3Assets.ఎరరగలబAudio), + }, + { + img: getAssetUrl(s3Assets.అమలపలకImg), + text: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + }, + { + img: getAssetUrl(s3Assets.సవరతజడImg), + text: "సవరంతో జడ", + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + }, + ], + correctWord: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.కడమదగడImg), + text: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + }, + { + img: getAssetUrl(s3Assets.వపకచదImg), + text: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + }, + { + img: getAssetUrl(s3Assets.సవరతజడImg), + text: "సవరంతో జడ", + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + }, + ], + correctWord: "సవరంతో జడ", + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చతలగడగImg), + text: "చేతిలో గొడుగు", + audio: getAssetAudioUrl(s3Assets.చతలగడగAudio), + }, + { + img: getAssetUrl(s3Assets.కడమదగడImg), + text: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + }, + { + img: getAssetUrl(s3Assets.తలలనఏనగImg), + text: "తెల్లని ఏనుగు", + audio: getAssetAudioUrl(s3Assets.తలలనఏనగAudio), + }, + ], + correctWord: "చేతిలో గొడుగు", + audio: getAssetAudioUrl(s3Assets.చతలగడగAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.గననలపలImg), + text: "గిన్నెలో పాలు", + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + }, + { + img: getAssetUrl(s3Assets.కడమదగడImg), + text: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + }, + { + img: getAssetUrl(s3Assets.పసరగరలImg), + text: "పెసర గారెలు", + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + }, + ], + correctWord: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio2), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ఎరరగలబImg), + text: "ఎర్ర గులాబి", + audio: getAssetAudioUrl(s3Assets.ఎరరగలబAudio), + }, + { + img: getAssetUrl(s3Assets.సవరతజడImg), + text: "సవరంతో జడ", + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + }, + { + img: getAssetUrl(s3Assets.కడమదగడImg), + text: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + }, + ], + correctWord: "ఎర్ర గులాబి", + audio: getAssetAudioUrl(s3Assets.ఎరరగలబAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.అమలపలకImg), + text: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + }, + { + img: getAssetUrl(s3Assets.బవగరకImg), + text: "బావి గిరక", + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + }, + { + img: getAssetUrl(s3Assets.గననలపలImg), + text: "గిన్నెలో పాలు", + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + }, + ], + correctWord: "గిన్నెలో పాలు", + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.అమలపలకImg), + text: "అమల పలక", + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + }, + { + img: getAssetUrl(s3Assets.పసరగరలImg), + text: "పెసర గారెలు", + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + }, + { + img: getAssetUrl(s3Assets.తలలనఏనగImg), + text: "తెల్లని ఏనుగు", + audio: getAssetAudioUrl(s3Assets.తలలనఏనగAudio), + }, + ], + correctWord: "తెల్లని ఏనుగు", + audio: getAssetAudioUrl(s3Assets.తలలనఏనగAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.సననయపటImg), + text: "సన్నాయి పాట", + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + }, + { + img: getAssetUrl(s3Assets.అకకజడImg), + text: "అక్క జడ", + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + }, + { + img: getAssetUrl(s3Assets.వపకచదImg), + text: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + }, + ], + correctWord: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.వపకచదImg), + text: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + }, + { + img: getAssetUrl(s3Assets.కడమదగడImg), + text: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + }, + { + img: getAssetUrl(s3Assets.గడమదబలలImg), + text: "గోడమీద బల్లి", + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + }, + ], + correctWord: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.తలమదటపImg), + text: "తలమీద టోపి", + audio: getAssetAudioUrl(s3Assets.తలమదటపAudio), + }, + { + img: getAssetUrl(s3Assets.వపకచదImg), + text: "వేపాకు చేదు", + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + }, + { + img: getAssetUrl(s3Assets.బదలనళలImg), + text: "బిందెలో నీళ్లు", + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + }, + ], + correctWord: "తలమీద టోపి", + audio: getAssetAudioUrl(s3Assets.తలమదటపAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.అకకజడImg), + text: "అక్క జడ", + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + }, + { + img: getAssetUrl(s3Assets.చటటమలకImg), + text: "చిట్టి మొలక", + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + }, + { + img: getAssetUrl(s3Assets.సననయపటImg), + text: "సన్నాయి పాట", + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + }, + ], + correctWord: "సన్నాయి పాట", + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చలకమకకImg), + text: "చిలుక ముక్కు", + audio: getAssetAudioUrl(s3Assets.చలకమకకAudio), + }, + { + img: getAssetUrl(s3Assets.సననయపటImg), + text: "సన్నాయి పాట", + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + }, + { + img: getAssetUrl(s3Assets.గడమదబలలImg), + text: "గోడమీద బల్లి", + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + }, + ], + correctWord: "గోడమీద బల్లి", + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.పలలపటటImg), + text: "పల్లి పట్టి", + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + }, + { + img: getAssetUrl(s3Assets.గడమదబలలImg), + text: "గోడమీద బల్లి", + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + }, + { + img: getAssetUrl(s3Assets.బదలనళలImg), + text: "బిందెలో నీళ్లు", + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + }, + ], + correctWord: "పల్లి పట్టి", + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.చటటమలకImg), + text: "చిట్టి మొలక", + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + }, + { + img: getAssetUrl(s3Assets.అకకజడImg), + text: "అక్క జడ", + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + }, + { + img: getAssetUrl(s3Assets.బదలనళలImg), + text: "బిందెలో నీళ్లు", + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + }, + ], + correctWord: "అక్క జడ", + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బదలనళలImg), + text: "బిందెలో నీళ్లు", + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + }, + { + img: getAssetUrl(s3Assets.చలకమకకImg), + text: "చిలుక ముక్కు", + audio: getAssetAudioUrl(s3Assets.చలకమకకAudio), + }, + { + img: getAssetUrl(s3Assets.కడమదగడImg), + text: "కొండమీద గుడి", + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + }, + ], + correctWord: "చిలుక ముక్కు", + audio: getAssetAudioUrl(s3Assets.చలకమకకAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.బదలనళలImg), + text: "బిందెలో నీళ్లు", + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + }, + { + img: getAssetUrl(s3Assets.గడమదబలలImg), + text: "గోడమీద బల్లి", + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + }, + { + img: getAssetUrl(s3Assets.పలలపటటImg), + text: "పల్లి పట్టి", + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + }, + ], + correctWord: "బిందెలో నీళ్లు", + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.సననయపటImg), + text: "సన్నాయి పాట", + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + }, + { + img: getAssetUrl(s3Assets.చటటమలకImg), + text: "చిట్టి మొలక", + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + }, + { + img: getAssetUrl(s3Assets.పలలపటటImg), + text: "పల్లి పట్టి", + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + }, + ], + correctWord: "చిట్టి మొలక", + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + flowName: "S2", + type: "soundMatch", + }, + ], + }, + kn: { + 1: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಔಡಲImg), + text: "ಔಡಲ", + audio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಫಲImg), + text: "ಫಲ", + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಝಳImg), + text: "ಝಳ", + audio: getAssetAudioUrl(s3Assets.ಝಳAudio), + } + ], + correctWord: "ಝಳ", + audio: getAssetAudioUrl(s3Assets.ಝಳAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ನಳImg), + text: "ನಳ", + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಝಳImg), + text: "ಝಳ", + audio: getAssetAudioUrl(s3Assets.ಝಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಯImg), + text: "ಹಯ", + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + } + ], + correctWord: "ನಳ", + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಫಲImg), + text: "ಫಲ", + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಯImg), + text: "ಹಯ", + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + }, + { + img: getAssetUrl(s3Assets.ನಳImg), + text: "ನಳ", + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + } + ], + correctWord: "ಫಲ", + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಗರಗಸImg), + text: "ಗರಗಸ", + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಯImg), + text: "ಹಯ", + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + }, + { + img: getAssetUrl(s3Assets.ಆವರಣImg), + text: "ಆವರಣ", + audio: getAssetAudioUrl(s3Assets.ಆವರಣAudio), + } + ], + correctWord: "ಹಯ", + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + }, + { + img: getAssetUrl(s3Assets.ನಳImg), + text: "ನಳ", + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಕದImg), + text: "ಕಂದ", + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + } + ], + correctWord: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕದImg), + text: "ಕಂದ", + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + }, + { + img: getAssetUrl(s3Assets.ಫಲImg), + text: "ಫಲ", + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + } + ], + correctWord: "ಕಂದ", + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ನಳImg), + text: "ನಳ", + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಕದImg), + text: "ಕಂದ", + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + }, + { + img: getAssetUrl(s3Assets.ತಬಲImg), + text: "ತಬಲ", + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + } + ], + correctWord: "ತಬಲ", + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಔಡಲImg), + text: "ಔಡಲ", + audio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + }, + { + img: getAssetUrl(s3Assets.ತಬಲImg), + text: "ತಬಲ", + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮಯImg), + text: "ಸಮಯ", + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + } + ], + correctWord: "ಔಡಲ", + audio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ನಳImg), + text: "ನಳ", + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಗರಗಸImg), + text: "ಗರಗಸ", + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + }, + { + img: getAssetUrl(s3Assets.ಫಲImg), + text: "ಫಲ", + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + } + ], + correctWord: "ಗರಗಸ", + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಆವರಣImg), + text: "ಆವರಣ", + audio: getAssetAudioUrl(s3Assets.ಆವರಣAudio), + }, + { + img: getAssetUrl(s3Assets.ಗರಗಸImg), + text: "ಗರಗಸ", + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + }, + { + img: getAssetUrl(s3Assets.ತಬಲImg), + text: "ತಬಲ", + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + } + ], + correctWord: "ಆವರಣ", + audio: getAssetAudioUrl(s3Assets.ಆವರಣAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮನImg), + text: "ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಯImg), + text: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + }, + { + img: getAssetUrl(s3Assets.ತರಣImg), + text: "ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + } + ], + correctWord: "ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ತಯImg), + text: "ತಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ತಯAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಯImg), + text: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + }, + { + img: getAssetUrl(s3Assets.ತರಣImg), + text: "ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + } + ], + correctWord: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕವರImg), + text: "ಕಾವೇರಿ", + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + }, + { + img: getAssetUrl(s3Assets.ನರImg), + text: "ನೂರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಯImg), + text: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + } + ], + correctWord: "ನೂರು", + audio: getAssetAudioUrl(s3Assets.ನರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಬಯImg), + text: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + }, + { + img: getAssetUrl(s3Assets.ಸದರImg), + text: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + }, + { + img: getAssetUrl(s3Assets.ತಯImg), + text: "ತಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ತಯAudio), + } + ], + correctWord: "ತಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ತಯAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕವರImg), + text: "ಕಾವೇರಿ", + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮನImg), + text: "ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಳImg), + text: "ಕೋಳಿ", + audio: getAssetAudioUrl(s3Assets.ಕಳAudio), + } + ], + correctWord: "ಕೋಳಿ", + audio: getAssetAudioUrl(s3Assets.ಕಳAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಎರಡImg), + text: "ಎರಡು", + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಮನImg), + text: "ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಯImg), + text: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + } + ], + correctWord: "ಎರಡು", + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸದರImg), + text: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಸದImg), + text: "ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + }, + { + img: getAssetUrl(s3Assets.ಮನImg), + text: "ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + } + ], + correctWord: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಸದImg), + text: "ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + }, + { + img: getAssetUrl(s3Assets.ತರಣImg), + text: "ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಯImg), + text: "ಬಾಯಿ", + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + } + ], + correctWord: "ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮನImg), + text: "ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + }, + { + img: getAssetUrl(s3Assets.ಎರಡImg), + text: "ಎರಡು", + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಸದImg), + text: "ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + } + ], + correctWord: "ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಸದImg), + text: "ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + }, + { + img: getAssetUrl(s3Assets.ಕವರImg), + text: "ಕಾವೇರಿ", + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + }, + { + img: getAssetUrl(s3Assets.ಸದರImg), + text: "ಸುಂದರ", + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + } + ], + correctWord: "ಕಾವೇರಿ", + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + flowName: "S2", + type: "soundMatch", + }, + ], + 2: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಪರವಳImg), + text: "ಪಾರಿವಾಳ", + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಲಲImg), + text: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಟಕImg), + text: "ಕಿಟಕಿ", + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + } + ], + correctWord: "ಕಿಟಕಿ", + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಚಲಪಲImg), + text: "ಚಿಲಿಪಿಲಿ", + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಪರವಳImg), + text: "ಪಾರಿವಾಳ", + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಧಕರImg), + text: "ಅಧಿಕಾರಿ", + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + } + ], + correctWord: "ಅಧಿಕಾರಿ", + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಸಬImg), + text: "ಮೂಸಂಬಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸಬAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಡಲImg), + text: "ಸೊಂಡಿಲು", + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಮಮImg), + text: "ಅಮ್ಮ", + audio: getAssetAudioUrl(s3Assets.ಅಮಮAudio), + } + ], + correctWord: "ಮೂಸಂಬಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸಬAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಚಲಪಲImg), + text: "ಚಿಲಿಪಿಲಿ", + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಗಡಡImg), + text: "ಗಡ್ಡ", + audio: getAssetAudioUrl(s3Assets.ಗಡಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಪರವಳImg), + text: "ಪಾರಿವಾಳ", + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + } + ], + correctWord: "ಪಾರಿವಾಳ", + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಸಬImg), + text: "ಮೂಸಂಬಿ", + audio: getAssetAudioUrl(s3Assets.ಮಸಬAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಡಲImg), + text: "ಸೊಂಡಿಲು", + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಮಮImg), + text: "ಅಮ್ಮ", + audio: getAssetAudioUrl(s3Assets.ಅಮಮAudio), + } + ], + correctWord: "ಅಮ್ಮ", + audio: getAssetAudioUrl(s3Assets.ಅಮಮAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಚಲಪಲImg), + text: "ಚಿಲಿಪಿಲಿ", + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಪದಯImg), + text: "ಪಂದ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಪದಯAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಟಕImg), + text: "ಕಿಟಕಿ", + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + } + ], + correctWord: "ಚಿಲಿಪಿಲಿ", + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸಡಲImg), + text: "ಸೊಂಡಿಲು", + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಹಲಲImg), + text: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಧಕರImg), + text: "ಅಧಿಕಾರಿ", + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + } + ], + correctWord: "ಸೊಂಡಿಲು", + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಟಕImg), + text: "ಕಿಟಕಿ", + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + }, + { + img: getAssetUrl(s3Assets.ಅಧಕರImg), + text: "ಅಧಿಕಾರಿ", + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + }, + { + img: getAssetUrl(s3Assets.ಪದಯImg), + text: "ಪಂದ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಪದಯAudio), + } + ], + correctWord: "ಪಂದ್ಯ", + audio: getAssetAudioUrl(s3Assets.ಪದಯAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹಲಲImg), + text: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಡಲImg), + text: "ಸೊಂಡಿಲು", + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಪರವಳImg), + text: "ಪಾರಿವಾಳ", + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + } + ], + correctWord: "ಹುಲ್ಲು", + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಟಕImg), + text: "ಕಿಟಕಿ", + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + }, + { + img: getAssetUrl(s3Assets.ಗಡಡImg), + text: "ಗಡ್ಡ", + audio: getAssetAudioUrl(s3Assets.ಗಡಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಡಲImg), + text: "ಸೊಂಡಿಲು", + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + } + ], + correctWord: "ಗಡ್ಡ", + audio: getAssetAudioUrl(s3Assets.ಗಡಡAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹತತImg), + text: "ಹತ್ತು", + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + }, + { + img: getAssetUrl(s3Assets.ಜತರImg), + text: "ಜಾತ್ರೆ", + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಷಕರImg), + text: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + } + ], + correctWord: "ಹತ್ತು", + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಜತರImg), + text: "ಜಾತ್ರೆ", + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಷಕರImg), + text: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + }, + { + img: getAssetUrl(s3Assets.ಶಬದImg), + text: "ಶಬ್ದ", + audio: getAssetAudioUrl(s3Assets.ಶಬದAudio), + } + ], + correctWord: "ಜಾತ್ರೆ", + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸಮದರImg), + text: "ಸಮುದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಹತತImg), + text: "ಹತ್ತು", + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + }, + { + img: getAssetUrl(s3Assets.ಶಬದImg), + text: "ಶಬ್ದ", + audio: getAssetAudioUrl(s3Assets.ಶಬದAudio), + } + ], + correctWord: "ಶಬ್ದ", + audio: getAssetAudioUrl(s3Assets.ಶಬದAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಗಗImg), + text: "ಮಗ್ಗ", + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮದರImg), + text: "ಸಮುದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಪಕಷImg), + text: "ಪಕ್ಷಿ", + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + } + ], + correctWord: "ಪಕ್ಷಿ", + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮಗಗImg), + text: "ಮಗ್ಗ", + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + }, + { + img: getAssetUrl(s3Assets.ಸನಹತImg), + text: "ಸ್ನೇಹಿತ", + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + }, + { + img: getAssetUrl(s3Assets.ಗದದಲImg), + text: "ಗುದ್ದಲಿ", + audio: getAssetAudioUrl(s3Assets.ಗದದಲAudio), + } + ], + correctWord: "ಗುದ್ದಲಿ", + audio: getAssetAudioUrl(s3Assets.ಗದದಲAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಹಳಳImg), + text: "ಹಳ್ಳಿ", + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮದರImg), + text: "ಸಮುದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಷಕರImg), + text: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + } + ], + correctWord: "ಹಳ್ಳಿ", + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಜತರImg), + text: "ಜಾತ್ರೆ", + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಗಗImg), + text: "ಮಗ್ಗ", + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + }, + { + img: getAssetUrl(s3Assets.ಹತತImg), + text: "ಹತ್ತು", + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + } + ], + correctWord: "ಮಗ್ಗ", + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಪಕಷImg), + text: "ಪಕ್ಷಿ", + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಷಕರImg), + text: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + }, + { + img: getAssetUrl(s3Assets.ಸನಹತImg), + text: "ಸ್ನೇಹಿತ", + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + } + ], + correctWord: "ಮುಷ್ಕರ", + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಗದದಲImg), + text: "ಗುದ್ದಲಿ", + audio: getAssetAudioUrl(s3Assets.ಗದದಲAudio), + }, + { + img: getAssetUrl(s3Assets.ಪಕಷImg), + text: "ಪಕ್ಷಿ", + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + }, + { + img: getAssetUrl(s3Assets.ಸಮದರImg), + text: "ಸಮುದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + } + ], + correctWord: "ಸಮುದ್ರ", + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಸನಹತImg), + text: "ಸ್ನೇಹಿತ", + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + }, + { + img: getAssetUrl(s3Assets.ಮಗಗImg), + text: "ಮಗ್ಗ", + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + }, + { + img: getAssetUrl(s3Assets.ಪಕಷImg), + text: "ಪಕ್ಷಿ", + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + } + ], + correctWord: "ಸ್ನೇಹಿತ", + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + flowName: "S2", + type: "soundMatch", + }, + ], + 3: [ + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅದದಸರImg), + text: "ಅಂದದ ಸರ", + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + }, + { + img: getAssetUrl(s3Assets.ಚದರಬದImg), + text: "ಚಂದಿರ ಬಂದ", + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + }, + { + img: getAssetUrl(s3Assets.ಮರಒಣಗದImg), + text: "ಮರ ಒಣಗಿದೆ", + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + } + ], + correctWord: "ಅಂದದ ಸರ", + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅದಗಡImg), + text: "ಅದು ಗುಡಿ", + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಚದರಬದImg), + text: "ಚಂದಿರ ಬಂದ", + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + }, + { + img: getAssetUrl(s3Assets.ಚಡಹImg), + text: "ಚೆಂಡು ಹೂ", + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + } + ], + correctWord: "ಚಂದಿರ ಬಂದ", + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಮವನಮರImg), + text: "ಮಾವಿನ ಮರ", + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + }, + { + img: getAssetUrl(s3Assets.ಗಳಯರಮತಕತImg), + text: "ಗೆಳೆಯರ ಮಾತುಕತೆ", + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + }, + { + img: getAssetUrl(s3Assets.ಮರಒಣಗದImg), + text: "ಮರ ಒಣಗಿದೆ", + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + } + ], + correctWord: "ಮರ ಒಣಗಿದೆ", + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅದಗಡImg), + text: "ಅದು ಗುಡಿ", + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಇದಮಸದImg), + text: "ಇದು ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + }, + { + img: getAssetUrl(s3Assets.ಉದಯನಮನImg), + text: "ಉದಯನ ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + } + ], + correctWord: "ಅದು ಗುಡಿ", + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಖಖಆಟImg), + text: "ಖೋ ಖೋ ಆಟ", + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + }, + { + img: getAssetUrl(s3Assets.ಚಡಹImg), + text: "ಚೆಂಡು ಹೂ", + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + }, + { + img: getAssetUrl(s3Assets.ಏಳಎಲಗಳImg), + text: "ಏಳು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + } + ], + correctWord: "ಚೆಂಡು ಹೂ", + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಚದರಬದImg), + text: "ಚಂದಿರ ಬಂದ", + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + }, + { + img: getAssetUrl(s3Assets.ಏಳಎಲಗಳImg), + text: "ಏಳು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಮರಒಣಗದImg), + text: "ಮರ ಒಣಗಿದೆ", + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + } + ], + correctWord: "ಏಳು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಅದಗಡImg), + text: "ಅದು ಗುಡಿ", + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + }, + { + img: getAssetUrl(s3Assets.ಚಡಹImg), + text: "ಚೆಂಡು ಹೂ", + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಗಲನತರಣImg), + text: "ಬಾಗಿಲಿನ ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + } + ], + correctWord: "ಬಾಗಿಲಿನ ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಆಕಶದತರಗಳImg), + text: "ಆಕಾಶದ ತಾರೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಮವನಮರImg), + text: "ಮಾವಿನ ಮರ", + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + }, + { + img: getAssetUrl(s3Assets.ಗಳಯರಮತಕತImg), + text: "ಗೆಳೆಯರ ಮಾತುಕತೆ", + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + } + ], + correctWord: "ಆಕಾಶದ ತಾರೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಇದಮಸದImg), + text: "ಇದು ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಡನಪರಣಗಳImg), + text: "ಕಾಡಿನ ಪ್ರಾಣಿಗಳು", + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಉದಯನಮನImg), + text: "ಉದಯನ ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + } + ], + correctWord: "ಕಾಡಿನ ಪ್ರಾಣಿಗಳು", + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಖಖಆಟImg), + text: "ಖೋ ಖೋ ಆಟ", + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + }, + { + img: getAssetUrl(s3Assets.ಏಳಎಲಗಳImg), + text: "ಏಳು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ನವಲನನರತನImg), + text: "ನವಿಲಿನ ನರ್ತನ", + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + } + ], + correctWord: "ನವಿಲಿನ ನರ್ತನ", + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + flowName: "S1", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ರಚಯದಸರImg), + text: "ರುಚಿಯಾದ ಸಾರು", + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + }, + { + img: getAssetUrl(s3Assets.ಬಗಲನತರಣImg), + text: "ಬಾಗಿಲಿನ ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + }, + { + img: getAssetUrl(s3Assets.ಆಕಶದತರಗಳImg), + text: "ಆಕಾಶದ ತಾರೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + } + ], + correctWord: "ರುಚಿಯಾದ ಸಾರು", + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಕಡನಪರಣಗಳImg), + text: "ಕಾಡಿನ ಪ್ರಾಣಿಗಳು", + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ದಸವಳಗಡಗಳImg), + text: "ದಾಸವಾಳ ಗಿಡಗಳು", + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ನವಲನನರತನImg), + text: "ನವಿಲಿನ ನರ್ತನ", + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + } + ], + correctWord: "ದಾಸವಾಳ ಗಿಡಗಳು", + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ರಚಯದಸರImg), + text: "ರುಚಿಯಾದ ಸಾರು", + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + }, + { + img: getAssetUrl(s3Assets.ದಸವಳಗಡಗಳImg), + text: "ದಾಸವಾಳ ಗಿಡಗಳು", + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಆಗಸದಚದರImg), + text: "ಆಗಸದ ಚಂದಿರ", + audio: getAssetAudioUrl(s3Assets.ಆಗಸದಚದರAudio), + } + ], + correctWord: "ಆಗಸದ ಚಂದಿರ", + audio: getAssetAudioUrl(s3Assets.ಆಗಸದಚದರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಉಯಯಲಆಡವನImg), + text: "ಉಯ್ಯಾಲೆ ಆಡುವೆನು", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲಆಡವನAudio), + }, + { + img: getAssetUrl(s3Assets.ನಲಕಎಲಗಳImg), + text: "ನಾಲ್ಕು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಅದದಸರImg), + text: "ಅಂದದ ಸರ", + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + } + ], + correctWord: "ಉಯ್ಯಾಲೆ ಆಡುವೆನು", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲಆಡವನAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಚದರಬದImg), + text: "ಚಂದಿರ ಬಂದ", + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + }, + { + img: getAssetUrl(s3Assets.ನಲಕಎಲಗಳImg), + text: "ನಾಲ್ಕು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಮರಒಣಗದImg), + text: "ಮರ ಒಣಗಿದೆ", + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + } + ], + correctWord: "ನಾಲ್ಕು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಬಗಲನತರಣImg), + text: "ಬಾಗಿಲಿನ ತೋರಣ", + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + }, + { + img: getAssetUrl(s3Assets.ಆಕಶದತರಗಳImg), + text: "ಆಕಾಶದ ತಾರೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಮವನಮರImg), + text: "ಮಾವಿನ ಮರ", + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + } + ], + correctWord: "ಮಾವಿನ ಮರ", + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಗಳಯರಮತಕತImg), + text: "ಗೆಳೆಯರ ಮಾತುಕತೆ", + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + }, + { + img: getAssetUrl(s3Assets.ಕಡನಪರಣಗಳImg), + text: "ಕಾಡಿನ ಪ್ರಾಣಿಗಳು", + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ನವಲನನರತನImg), + text: "ನವಿಲಿನ ನರ್ತನ", + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + } + ], + correctWord: "ಗೆಳೆಯರ ಮಾತುಕತೆ", + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ರಚಯದಸರImg), + text: "ರುಚಿಯಾದ ಸಾರು", + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + }, + { + img: getAssetUrl(s3Assets.ಇದಮಸದImg), + text: "ಇದು ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + }, + { + img: getAssetUrl(s3Assets.ದಸವಳಗಡಗಳImg), + text: "ದಾಸವಾಳ ಗಿಡಗಳು", + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + } + ], + correctWord: "ಇದು ಮಸೀದಿ", + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಆಗಸದಚದರImg), + text: "ಆಗಸದ ಚಂದಿರ", + audio: getAssetAudioUrl(s3Assets.ಆಗಸದಚದರAudio), + }, + { + img: getAssetUrl(s3Assets.ಉಯಯಲಆಡವನImg), + text: "ಉಯ್ಯಾಲೆ ಆಡುವೆನು", + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲಆಡವನAudio), + }, + { + img: getAssetUrl(s3Assets.ಉದಯನಮನImg), + text: "ಉದಯನ ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + } + ], + correctWord: "ಉದಯನ ಮನೆ", + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + flowName: "S2", + type: "soundMatch", + }, + { + allwords: [ + { + img: getAssetUrl(s3Assets.ಖಖಆಟImg), + text: "ಖೋ ಖೋ ಆಟ", + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + }, + { + img: getAssetUrl(s3Assets.ನಲಕಎಲಗಳImg), + text: "ನಾಲ್ಕು ಎಲೆಗಳು", + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + }, + { + img: getAssetUrl(s3Assets.ಅದದಸರImg), + text: "ಅಂದದ ಸರ", + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + } + ], + correctWord: "ಖೋ ಖೋ ಆಟ", + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + flowName: "S2", + type: "soundMatch", + } + ], + }, +}; + +// Sound Hunt (Picture words) - Read the word and choose the right sound +const pictureWordsContent = { + en: { + 1: [ + //S1 + { + word: "bad", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.cookAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.godAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.badAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "mom", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.godAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.momAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.goatAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "hop", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.hopAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.fatAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.cookAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "fat", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.fatAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.momAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.sadAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "him", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.goatAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.nineAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.himAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "sad", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.godAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.fatAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.sadAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "cook", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.godAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.cookAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.goatAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "god", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.godAudio2), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.nineAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.fatAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "nine", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.cookAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.nineAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.fatAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "goat", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.goatAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.badAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.cookAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + //S2 + { + word: "buy", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.wideAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.noteAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.buyAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "fine", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.happyAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.wideAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.fineAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "kind", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.kindAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.bodyAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.buyAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "note", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.fineAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.halfAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.noteAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "wide", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.happyAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.wideAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.hideAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "know", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.halfAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.knowAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.hideAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "half", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.fineAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.halfAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.bodyAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "hide", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.buyAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.halfAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.hideAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "happy", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.knowAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.happyAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.halfAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "body", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.halfAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.bodyAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.knowAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + ], + 2: [ + { + word: "Son", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.sonAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.chairAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.fairAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "zig", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.zigAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.fairAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.chatAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "log", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.logAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.sonAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.birdAudio2), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "now", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.penAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.nowAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.fairAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "chat", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.chatAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.sonAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.nowAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "pen", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.logAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.chatAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.penAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "fair", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.nowAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.birdAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.fairAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "care", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.birdAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.careAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.zigAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "bird", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.zigAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.chatAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.birdAudio2), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "chair", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.careAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.logAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.chairAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "turn", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.orangeAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.turnAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.dearAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "soothe", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.earthAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.sootheAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.dearAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "perk", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.lazyAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.perkAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.earAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "dear", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.earthAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.royalAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.dearAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "royal", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.perkAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.royalAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ear", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.dearAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.earAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.royalAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "lazy", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.lazyAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.earthAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "orange", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.sootheAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.perkAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.orangeAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "purple", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.earAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.perkAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "earth", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.purpleAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.earthAudio2), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.lazyAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + ], + }, + te: { + 1: [ + { + word: "ఝషం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.జకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ఝషAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.కజరAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "గంట", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.మడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.గటAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "పడవ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.జమAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పడవAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "తలగడ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.గటAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.తలగడAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.దడAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "జామ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.జమAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.దడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పడవAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "జలం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.కజరAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.జలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.పడవAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "జింక", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.జకAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.గటAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పడవAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "దండం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.దడAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.జమAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.జలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "కంజర", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.మడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.దడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కజరAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "మూడు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.పడవAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.మడAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.జలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "కోడి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పజరAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కడAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "తాళం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.తళAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.చయAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "చేయి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చయAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.కలశAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ఉడుత", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చయAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "కోతులు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.కతలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "సినిమా", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.సనమAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కలశAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "నూరు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.కలశAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కతలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "కలశం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.కలశAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.నరAudio2), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కతలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ఏనుగు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.కతలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "పంజరం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ఉడతAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పజర2Audio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ఏనగAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + ], + 2: [ + { + word: "కిరీటం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చకకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వమనAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కరటAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "మూకుడు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.పజరAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చకకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.మకడAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "అభినయం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.మసలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.వమనAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "తప్పు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.పజరAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.తపపAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.కరటAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "చెక్క", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చకకAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.మకడAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "మొసలి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.మసలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.పజరAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చకకAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "బెండకాయ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.బమమAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "విమానం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.వమనAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.తపపAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.మసలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "బొమ్మ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చకకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బమమAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.మసలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "పూజారి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.అభనయAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పజరAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.బడకయAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "దర్వాజ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.వకషAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "వాద్యాల", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వననలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "వృక్షం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వకషAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ప్రపంచం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "సిరిమల్లె", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చననAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.దరవజAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "బియ్యం", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.వదయలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బయయAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.చననAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ఇల్లు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.వననలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "చిన్ని", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చననAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "బుర్రకథ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చననAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ఇలలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బరరకథAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "వెన్నెల", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.పరపచAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.సరమలలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వననలAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + ], + 3: [ + { + word: "వేపాకు చేదు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "బావి గిరక", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "పెసర గారెలు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "అమల పలక", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ఎరరగలబAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "సవరంతో జడ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "చేతిలో గొడుగు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చతలగడగAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.తలలనఏనగAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "కొండమీద గుడి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ఎర్ర గులాబి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ఎరరగలబAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.సవరతజడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + isCorrect: false, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "గిన్నెలో పాలు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బవగరకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.గననలపలAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "తెల్లని ఏనుగు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.అమలపలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పసరగరలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.తలలనఏనగAudio), + isCorrect: true, + }, + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "వేపాకు చేదు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "కొండమీద గుడి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "తలమీద టోపి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.తలమదటపAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.వపకచదAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "సన్నాయి పాట", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "గోడమీద బల్లి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చలకమకకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + isCorrect: true, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "పల్లి పట్టి", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "అక్క జడ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.అకకజడAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "చిలుక ముక్కు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చలకమకకAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.కడమదగడAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "బిందెలో నీళ్లు", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.బదలనళలAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.గడమదబలలAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "చిట్టి మొలక", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.సననయపటAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.చటటమలకAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.పలలపటటAudio), + isCorrect: false, + }, + ], + flowName: "S2", + type: "pictureWords", + }, + ], + }, + kn: { + 1: [ + { + word: "ಝಳ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಝಳAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ನಳ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಝಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಫಲ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಹಯ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹಯAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಆವರಣAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಸಮಯ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಕಂದ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ತಬಲ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಔಡಲ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಔಡಲAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಮಯAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಗರಗಸ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ನಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಫಲAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಆವರಣ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಆವರಣAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗರಗಸAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ತಬಲAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಮನೆ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಬಾಯಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ತಯAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ನೂರು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ನರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ತಾಯಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ತಯAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಕೋಳಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕಳAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಎರಡು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಸುಂದರ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ತೋರಣ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ತರಣAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಯAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಮಸೀದಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮನAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಎರಡAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಕಾವೇರಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮಸದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕವರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸದರAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + } + ], + 2: [ + { + word: "ಕಿಟಕಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಅಧಿಕಾರಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಮೂಸಂಬಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮಸಬAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅಮಮAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಪಾರಿವಾಳ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗಡಡAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಅಮ್ಮ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮಸಬAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅಮಮAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಚಿಲಿಪಿಲಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಚಲಪಲAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪದಯAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಸೊಂಡಿಲು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಪಂದ್ಯ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅಧಕರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪದಯAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಹುಲ್ಲು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಹಲಲAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪರವಳAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಗಡ್ಡ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಕಟಕAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗಡಡAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಡಲAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಹತ್ತು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಜಾತ್ರೆ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಶಬದAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಶಬ್ದ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಶಬದAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಪಕ್ಷಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಗುದ್ದಲಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗದದಲAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಹಳ್ಳಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಹಳಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಮಗ್ಗ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಜತರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಹತತAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಮುಷ್ಕರ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಷಕರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಸಮುದ್ರ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಗದದಲAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಸಮದರAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಸ್ನೇಹಿತ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಸನಹತAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮಗಗAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಪಕಷAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + ], + 3: [ + { + word: "ಅಂದದ ಸರ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಚಂದಿರ ಬಂದ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಮರ ಒಣಗಿದೆ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಅದು ಗುಡಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಚೆಂಡು ಹೂ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಏಳು ಎಲೆಗಳು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಬಾಗಿಲಿನ ತೋರಣ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಅದಗಡAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಚಡಹAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಆಕಾಶದ ತಾರೆಗಳು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ಕಾಡಿನ ಪ್ರಾಣಿಗಳು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + isCorrect: false, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ನವಿಲಿನ ನರ್ತನ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಏಳಎಲಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + isCorrect: true, + } + ], + flowName: "S1", + type: "pictureWords", + }, + { + word: "ರುಚಿಯಾದ ಸಾರು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ದಾಸವಾಳ ಗಿಡಗಳು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಆಗಸದ ಚಂದಿರ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಆಗಸದಚದರAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಉಯ್ಯಾಲೆ ಆಡುವೆನು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲಆಡವನAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ನಾಲ್ಕು ಎಲೆಗಳು", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಚದರಬದAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮರಒಣಗದAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಮಾವಿನ ಮರ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಬಗಲನತರಣAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಆಕಶದತರಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಮವನಮರAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಗೆಳೆಯರ ಮಾತುಕತೆ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಗಳಯರಮತಕತAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ಕಡನಪರಣಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ನವಲನನರತನAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಇದು ಮಸೀದಿ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ರಚಯದಸರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಇದಮಸದAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ದಸವಳಗಡಗಳAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಉದಯನ ಮನೆ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಆಗಸದಚದರAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಉಯಯಲಆಡವನAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಉದಯನಮನAudio), + isCorrect: true, + } + ], + flowName: "S2", + type: "pictureWords", + }, + { + word: "ಖೋ ಖೋ ಆಟ", + audioOptions: [ + { + audio: getAssetAudioUrl(s3Assets.ಖಖಆಟAudio), + isCorrect: true, + }, + { + audio: getAssetAudioUrl(s3Assets.ನಲಕಎಲಗಳAudio), + isCorrect: false, + }, + { + audio: getAssetAudioUrl(s3Assets.ಅದದಸರAudio), + isCorrect: false, + } + ], + flowName: "S2", + type: "pictureWords", + } + ], + } +}; + +// Helper function to randomly select N items from an array +const getRandomItems = (array, count) => { + const shuffled = [...array].sort(() => Math.random() - 0.5); + return shuffled.slice(0, Math.min(count, array.length)); +}; + +const SoundHuntS1Combined = ({ + setVoiceText, + setRecordedAudio, + setVoiceAnimate, + storyLine, + type, + handleNext, + background, + parentWords = "", + enableNext, + showTimer, + points, + steps, + currentStep, + contentId, + contentType, + level, + currentLevel, + isDiscover, + progressData, + showProgress, + playTeacherAudio = () => {}, + callUpdateLearner, + disableScreen, + isShowCase, + startShowCase, + setStartShowCase, + handleBack, + setEnableNext, + loading, + setOpenMessageDialog, + audio, + currentImg, + rStep, + vocabCount, + wordCount, +}) => { + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [selectedWord, setSelectedWord] = useState(null); + const [selectedAudioIndex, setSelectedAudioIndex] = useState(null); + const [showConfetti, setShowConfetti] = useState(false); + const [wrongWord, setWrongWord] = useState(null); + const [wrongAudioIndex, setWrongAudioIndex] = useState(null); + const [recording, setRecording] = useState("no"); + const navigate = useNavigate(); + const [isPlaying, setIsPlaying] = useState(false); + const [isAudioPlayedOnce, setIsAudioPlayedOnce] = useState(false); + const [playingAudioIndex, setPlayingAudioIndex] = useState(null); + const [scale, setScale] = useState(1); + // Track word selections for ansSelectionStatus - now an array of objects + const [ansSelectionStatus, setAnsSelectionStatus] = useState([]); + // Track if an option has been selected (to show Next button) + const [hasSelectedOption, setHasSelectedOption] = useState(false); + // Track game over data for showcase end screen + const [gameOverData, setGameOverData] = useState(null); + // Track if S1 completion has been processed (to prevent multiple addLesson calls) + const [isS1Completed, setIsS1Completed] = useState(false); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); + + // Get language from localStorage (accessible throughout component) + const language = getLocalData("lang") || "en"; + + // Filter content based on milestone level and randomly select questions + // Content selection logic: + // 1. Get milestone level (M1 or M2) from level prop + // 2. Filter content by flowName based on milestone: + // - M1 (level 1): S1 step → show flowName "S1" + // - M2 (level 2): S1 step → show flowName "S2" + // 3. Get language-specific content + // 4. Filter by target flowName (S1 or S2) + // 5. Randomly select 10 from each filtered list + // 6. Combine: first 10 soundMatch, then 10 pictureWords + const filteredContent = useMemo(() => { + // Use language from component scope + + // Use currentLevel prop directly - it's already "S1" or "S2" or "P1", etc. + const targetFlowName = + currentLevel === "S1" ? "S1" : currentLevel === "S2" ? "S2" : "S1"; + + // Get soundMatch content for the current language and level + const soundMatchForLevel = soundMatchContent[language]?.[level] || []; + + // Filter soundMatch content by target flowName + const soundMatchFiltered = soundMatchForLevel.filter( + (item) => item.flowName === targetFlowName + ); + + // Get pictureWords content for the current language and level + const pictureWordsForLevel = pictureWordsContent[language]?.[level] || []; + + // Filter pictureWords content by target flowName + const pictureWordsFiltered = pictureWordsForLevel.filter( + (item) => item.flowName === targetFlowName + ); + + // Randomly select 10 from soundMatch (or all if less than 10) + const randomSoundMatch = getRandomItems(soundMatchFiltered, 10); + + // Randomly select 10 from pictureWords (or all if less than 10) + const randomPictureWords = getRandomItems(pictureWordsFiltered, 10); + + // Combine: first 10 soundMatch, then 10 pictureWords + return [...randomSoundMatch, ...randomPictureWords]; + }, [level, currentLevel, language]); + + // Audio recording state + const mediaRecorderRef = React.useRef(null); + const recordedChunksRef = React.useRef([]); + const streamRef = React.useRef(null); + + useEffect(() => { + const interval = setInterval(() => { + setScale((prev) => (prev === 1 ? 1.2 : 1)); + }, 500); + + return () => clearInterval(interval); + }, []); + + // Use filteredContent instead of combinedContent + const currentQuestion = filteredContent[currentQuestionIndex]; + const isSoundMatch = currentQuestion?.type === "soundMatch"; + const isPictureWords = currentQuestion?.type === "pictureWords"; + + // Handle showcase end screen - call handleNext when user clicks button + useEffect(() => { + if (gameOverData && isShowCase && handleNext) { + // When gameOverData is set, MainLayout will show end screen + // When user clicks button, MainLayout navigates to "/_practice" + // We'll handle this in the parent component or use a custom handler + // For now, we'll set a flag that the parent can check + console.log( + "Showcase end screen shown - waiting for user to click button" + ); + } + }, [gameOverData, isShowCase, handleNext]); + + // Reset state when question changes + useEffect(() => { + setSelectedWord(null); + setSelectedAudioIndex(null); + setWrongWord(null); + setWrongAudioIndex(null); + setShowConfetti(false); + setRecording("no"); + setIsPlaying(false); + setIsAudioPlayedOnce(false); + setPlayingAudioIndex(null); + setHasSelectedOption(false); + + // Stop any active recording + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + mediaRecorderRef.current.stop(); + mediaRecorderRef.current = null; + } + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + streamRef.current = null; + } + }, [currentQuestionIndex]); + + // Cleanup on unmount + useEffect(() => { + return () => { + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + mediaRecorderRef.current.stop(); + mediaRecorderRef.current = null; + } + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + streamRef.current = null; + } + }; + }, []); + + // Handle Word Hunt (Sound Match) - Listen to Sound and choose the right word + const handleWordClick = async (word) => { + // Prevent multiple selections + if (hasSelectedOption) { + return; + } + + setSelectedWord(word); + const currentQuestion = filteredContent[currentQuestionIndex]; + const wordLower = word?.toLowerCase(); + const correctWordLower = currentQuestion.correctWord?.toLowerCase(); + // Use case-insensitive comparison to handle words like "Son" vs "son" + const isCorrect = wordLower === correctWordLower; + + // Track selection status for ansSelectionStatus + // Track only the selected word (one entry per question) + let currentSelection = null; + if (wordLower) { + currentSelection = { + text: wordLower, + status: isCorrect, + gameType: "SoundMatch", + }; + setAnsSelectionStatus((prev) => { + return [...prev, currentSelection]; + }); + } + + // Mark that an option has been selected + setHasSelectedOption(true); + + // Automatically move to next question after a short delay + setTimeout(async () => { + if (currentQuestionIndex === filteredContent.length - 1) { + // Last question - complete S1 + // Prevent multiple completion calls + if (isS1Completed) { + console.log("S1 already completed, skipping duplicate completion"); + return; + } + + setIsS1Completed(true); + + try { + // First update learner profile (this is called inside handleS1Complete) + // Pass currentSelection to ensure it's included in the API call + const result = await handleS1Complete(currentSelection); + // handleS1Complete already calls updateLearnerProfileOnCompletion() first + + // Check sessionResult from API response + const getSetData = result?.data || result; + const sessionResult = getSetData?.sessionResult; + const userWon = sessionResult?.toLowerCase() === "pass"; + const isFail = sessionResult?.toLowerCase() === "fail"; + + // If pass, reset lesson progress and update milestone level + if (userWon) { + console.log( + "S1 passed - resetting lesson progress and updating milestone level" + ); + + // Get milestoneLevel from getSetResult API response + const milestoneLevelFromAPI = + getSetData?.milestoneLevel || + getSetData?.milestone_level || + (level ? `m${level}` : "m1"); + + const sessionId = getLocalData("sessionId"); + const lang = getLocalData("lang") || "en"; + + // Reset lesson progress to 0 and update milestone level + try { + await addLesson({ + sessionId: sessionId, + milestone: "showcase", // S1 is a showcase step + lesson: 0, // Reset lesson progress to 0 + progress: 0, // Reset progress to 0 + language: lang, + milestoneLevel: milestoneLevelFromAPI, // Use milestoneLevel from getSetResult API + }); + console.log( + "addLesson completed - lesson progress reset and milestone level updated:", + milestoneLevelFromAPI + ); + } catch (addLessonError) { + console.error("Error calling addLesson on pass:", addLessonError); + // Continue even if addLesson fails + } + + // For showcase mode, show end screen + if (isShowCase) { + console.log("S1 showcase passed - showing end screen"); + setGameOverData({ + userWon: true, + link: "/_practice", // MainLayout will navigate here, parent will handle handleNext + }); + return; // Don't navigate yet - wait for user to click button on end screen + } + + // For non-showcase mode, navigate immediately + console.log("S1 passed - navigating to discover-start"); + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + return; + } + + // If fail, call addLesson then show end screen or call handleNext + if (isFail) { + console.log("S1 failed - calling addLesson (once)"); + const sessionId = getLocalData("sessionId"); + const lang = getLocalData("lang") || "en"; + const milestoneLevel = level ? `m${level}` : "m1"; + + // Find S1 step index in practiceSteps + const s1StepIndex = practiceSteps.findIndex( + (step) => step.title === "S1" + ); + const stepIndex = progressData?.currentPracticeStep || 0; + + // Calculate progress (S1 is a showcase step) + const totalSteps = practiceSteps.length; + const progress = Math.round( + ((stepIndex + 1) / (totalSteps * (steps || 1))) * 100 + ); + + try { + await addLesson({ + sessionId: sessionId, + milestone: "showcase", // S1 is a showcase step + lesson: stepIndex == 9 ? 0 : stepIndex + 1, + progress: Math.min(100, progress), + language: lang, + milestoneLevel: milestoneLevel, + }); + console.log("addLesson completed successfully"); + } catch (addLessonError) { + console.error("Error calling addLesson:", addLessonError); + // Continue even if addLesson fails + } + + // For showcase mode, show end screen + if (isShowCase) { + console.log("S1 showcase failed - showing end screen"); + setGameOverData({ + userWon: false, + link: "/_practice", // MainLayout will navigate here, parent will handle handleNext + }); + return; // Don't call handleNext yet - wait for user to click button on end screen + } + + // For non-showcase mode, call handleNext immediately + console.log("S1 failed - addLesson done - calling handleNext"); + if (handleNext && typeof handleNext === "function") { + await handleNext(true); + } else { + // Fallback: navigate to discover-start if handleNext is not available + console.log( + "handleNext not available - navigating to discover-start" + ); + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + } + } + } catch (error) { + console.error("Error handling S1 completion:", error); + // On error, try to call handleNext if available + if (handleNext && typeof handleNext === "function") { + await handleNext(true); + } else { + // Fallback: navigate to discover-start + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + } + } + } else { + // Move to next question + setCurrentQuestionIndex((prevIndex) => prevIndex + 1); + setSelectedWord(null); + setSelectedAudioIndex(null); + setHasSelectedOption(false); + setIsAudioPlayedOnce(false); + setIsPlaying(false); + setPlayingAudioIndex(null); + } + }, 500); // Small delay to show selection + }; + + // Handle Sound Hunt (Picture words) - Read the word and choose the right sound + const handleAudioClick = async (audioIndex) => { + // Prevent multiple selections + if (hasSelectedOption) { + return; + } + + setSelectedAudioIndex(audioIndex); + const currentQuestion = filteredContent[currentQuestionIndex]; + const selectedAudio = currentQuestion.audioOptions[audioIndex]; + const isCorrect = selectedAudio.isCorrect; + const wordLower = currentQuestion.word?.toLowerCase(); + + // Show visual feedback for correct/incorrect + if (isCorrect) { + setShowConfetti(true); + setTimeout(() => setShowConfetti(false), 2000); + } else { + setWrongAudioIndex(audioIndex); + setTimeout(() => setWrongAudioIndex(null), 1000); + } + + // Track selection status for ansSelectionStatus + let currentSelection = null; + if (wordLower) { + currentSelection = { + text: wordLower, + status: isCorrect, + gameType: "PictureWords", + }; + setAnsSelectionStatus((prev) => { + return [...prev, currentSelection]; + }); + } + + // Mark that an option has been selected + setHasSelectedOption(true); + + // Automatically move to next question after a short delay + setTimeout(async () => { + if (currentQuestionIndex === filteredContent.length - 1) { + // Last question - complete S1 + // Prevent multiple completion calls + if (isS1Completed) { + console.log("S1 already completed, skipping duplicate completion"); + return; + } + + setIsS1Completed(true); + + try { + // First update learner profile (this is called inside handleS1Complete) + // Pass currentSelection to ensure it's included in the API call + const result = await handleS1Complete(currentSelection); + // handleS1Complete already calls updateLearnerProfileOnCompletion() first + + // Check sessionResult from API response + const getSetData = result?.data || result; + const sessionResult = getSetData?.sessionResult; + const userWon = sessionResult?.toLowerCase() === "pass"; + const isFail = sessionResult?.toLowerCase() === "fail"; + + // If pass, reset lesson progress and update milestone level + if (userWon) { + console.log( + "S1 passed - resetting lesson progress and updating milestone level" + ); + + // Get milestoneLevel from getSetResult API response + const milestoneLevelFromAPI = + getSetData?.currentLevel || (level ? `m${level}` : "m1"); + + const sessionId = getLocalData("sessionId"); + const lang = getLocalData("lang") || "en"; + + // Reset lesson progress to 0 and update milestone level + try { + await addLesson({ + sessionId: sessionId, + milestone: "practice", // S1 is a showcase step + lesson: 0, // Reset lesson progress to 0 + progress: 0, // Reset progress to 0 + language: lang, + milestoneLevel: milestoneLevelFromAPI, // Use milestoneLevel from getSetResult API + }); + console.log( + "addLesson completed - lesson progress reset and milestone level updated:", + milestoneLevelFromAPI + ); + if (level === 3 || level === 6 || level === 9) { + setGameOverData({ + userWon: true, + link: "/assesment-end", + }); + setLocalData("tFlow", true); + return; // Exit to show feedback screen for M3/M6/M9 + } + } catch (addLessonError) { + console.error("Error calling addLesson on pass:", addLessonError); + // Continue even if addLesson fails + } + + // For showcase mode, show end screen + if (isShowCase) { + console.log("S1 showcase passed - showing end screen"); + setGameOverData({ + userWon: true, + link: "/_practice", // MainLayout will navigate here, parent will handle handleNext + }); + return; // Don't navigate yet - wait for user to click button on end screen + } + + // For non-showcase mode, navigate immediately + console.log("S1 passed - navigating to discover-start"); + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + return; + } + + // If fail, call addLesson then show end screen or call handleNext + if (isFail) { + console.log("S1 failed - calling addLesson (once)"); + const sessionId = getLocalData("sessionId"); + const lang = getLocalData("lang") || "en"; + const milestoneLevel = level ? `m${level}` : "m1"; + + // Find S1 step index in practiceSteps + const s1StepIndex = practiceSteps.findIndex( + (step) => step.title === "S1" + ); + const stepIndex = progressData?.currentPracticeStep || 0; + + // Calculate progress (S1 is a showcase step) + const totalSteps = practiceSteps.length; + const progress = Math.round( + (Math.min(stepIndex + 2, totalSteps) / totalSteps) * 100 + ); + + try { + await addLesson({ + sessionId: sessionId, + milestone: "practice", // S1 is a showcase step + lesson: stepIndex == 9 ? 0 : stepIndex + 1, + progress: stepIndex == 9 ? 0 : Math.min(100, progress), + language: lang, + milestoneLevel: milestoneLevel, + }); + console.log("addLesson completed successfully"); + } catch (addLessonError) { + console.error("Error calling addLesson:", addLessonError); + // Continue even if addLesson fails + } + + // For showcase mode, show end screen + if (isShowCase) { + console.log("S1 showcase failed - showing end screen"); + setGameOverData({ + userWon: false, + link: "/_practice", // MainLayout will navigate here, parent will handle handleNext + }); + return; // Don't call handleNext yet - wait for user to click button on end screen + } + + // For non-showcase mode, call handleNext immediately + console.log("S1 failed - addLesson done - calling handleNext"); + if (handleNext && typeof handleNext === "function") { + await handleNext(true); + } else { + // Fallback: navigate to discover-start if handleNext is not available + console.log( + "handleNext not available - navigating to discover-start" + ); + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + } + } + } catch (error) { + console.error("Error handling S1 completion:", error); + // On error, try to call handleNext if available + if (handleNext && typeof handleNext === "function") { + await handleNext(true); + } else { + // Fallback: navigate to discover-start + setLocalData("rFlow", false); + setLocalData("mFail", false); + setLocalData("rStep", 0); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + } + } + } else { + // Move to next question + setCurrentQuestionIndex((prevIndex) => prevIndex + 1); + setSelectedWord(null); + setSelectedAudioIndex(null); + setHasSelectedOption(false); + setIsAudioPlayedOnce(false); + setIsPlaying(false); + setPlayingAudioIndex(null); + } + }, 500); // Small delay to show selection + }; + + const handlePlayAudio = (audioIndex) => { + const currentQuestion = filteredContent[currentQuestionIndex]; + const audioOption = currentQuestion.audioOptions[audioIndex]; + + const audio = new Audio(audioOption.audio); + setPlayingAudioIndex(audioIndex); + + audio.play(); + + audio.onended = () => { + setPlayingAudioIndex(null); + }; + }; + + const handlePlayMainAudio = () => { + const currentQuestion = filteredContent[currentQuestionIndex]; + const audio = new Audio(currentQuestion.audio); + + audio.play(); + setIsPlaying(true); + setIsAudioPlayedOnce(true); + + audio.onended = () => { + setIsPlaying(false); + }; + }; + + const flowNames = [...new Set(filteredContent.map((item) => item.flowName))]; + const activeFlow = + filteredContent[currentQuestionIndex]?.flowName || flowNames[0]; + + // Convert blob to base64 + const blobToBase64 = (blob) => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + const base64data = reader.result.split(",")[1]; + resolve(base64data); + }; + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + }; + + // Start audio recording + const startRecording = async () => { + try { + recordedChunksRef.current = []; + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + streamRef.current = stream; + + const mimeType = "audio/webm;codecs=opus"; + const recorder = new MediaRecorder(stream, { mimeType }); + + recorder.ondataavailable = (event) => { + if (event.data && event.data.size > 0) { + recordedChunksRef.current.push(event.data); + } + }; + + recorder.onstop = () => { + console.log("Recording stopped event fired"); + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + streamRef.current = null; + } + }; + + mediaRecorderRef.current = recorder; + recorder.start(100); + console.log("Recording started"); + } catch (error) { + console.error("Error starting recording:", error); + } + }; + + // Stop audio recording and process + const stopRecording = async () => { + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + console.log("Stopping recording..."); + const recorder = mediaRecorderRef.current; + recorder.stop(); + + // Wait for the recorder to stop and process the audio + return new Promise((resolve) => { + const checkInterval = setInterval(() => { + if (recorder.state === "inactive") { + clearInterval(checkInterval); + console.log("Recording stopped, processing audio..."); + + // Just stop recording, don't call API here + // API will be called on S1 completion + resolve(); + + mediaRecorderRef.current = null; + recordedChunksRef.current = []; + } + }, 100); + + // Timeout after 2 seconds + setTimeout(() => { + clearInterval(checkInterval); + resolve(); + }, 2000); + }); + } else { + console.warn("No active recording to stop"); + return Promise.resolve(); + } + }; + + // Build ansSelectionStatus with all words from S1 questions + const buildAnsSelectionStatus = (currentSelection = null) => { + // If currentSelection is provided, include it in the array + // This ensures the last selection is included even if state hasn't updated yet + if (currentSelection) { + return [...ansSelectionStatus, currentSelection]; + } + // Return the array directly - it already contains all selections with gameType + return ansSelectionStatus; + }; + + // Update learner profile after S1 completion + const updateLearnerProfileOnCompletion = async (currentSelection = null) => { + try { + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + const sub_session_id = getLocalData("sub_session_id"); + + // Build ansSelectionStatus with all words from S1 + // Include currentSelection if provided (for the last question) + const ansSelectionStatusObj = buildAnsSelectionStatus(currentSelection); + + const requestBody = { + original_text: "Char", + audio: "", + session_id: sessionId, + language: lang, + date: new Date(), + sub_session_id, + contentType: "Char", + mechanics_id: "", + milestone: "B", + is_nonAsr: true, + ansSelectionStatus: ansSelectionStatusObj, + }; + + console.log("Calling updateLearnerProfile API on S1 completion:", { + sessionId, + sub_session_id, + lang, + ansSelectionStatus: ansSelectionStatusObj, + }); + + const result = await updateLearnerProfile(lang, requestBody); + console.log( + "✅ Learner profile updated successfully on S1 completion:", + result + ); + return result; + } catch (error) { + console.error("❌ Error updating learner profile on completion:", error); + console.error("Error details:", error.response || error.message); + throw error; + } + }; + + // Get/Set result when S1 is complete + const handleS1Complete = async (currentSelection = null) => { + try { + // First update learner profile + // Pass currentSelection to ensure the last answer is included + await updateLearnerProfileOnCompletion(currentSelection); + + // Then get/set result + const sub_session_id = getLocalData("sub_session_id"); + const sessionId = getLocalData("sessionId"); + + const result = await getSetResultPractice({ + subSessionId: sub_session_id, + currentContentType: contentType || "Word", + sessionId, + totalSyllableCount: 20, // Total questions for S1 combined + mechanism: { id: "soundHuntS1Combined", name: "soundHuntS1Combined" }, + }); + + console.log("S1 result:", result); + + // Call engagement predictor after getsetresult + // Interactions and lesson are automatically retrieved + callEngagementPredictor(sub_session_id); + + setLocalData("previous_level", result?.data?.previous_level); + setLocalData("s1_complete", true); + setLocalData("s1_result", JSON.stringify(result)); + + // Return the result so caller can check sessionResult + return result; + } catch (error) { + console.error("Error getting/setting S1 result:", error); + throw error; + } + }; + + return ( + +
+ {recording === "no" && ( + <> +
+ {[ + { top: "10%", left: "5%" }, + { top: "25%", left: "30%" }, + { top: "10%", left: "55%" }, + { top: "25%", left: "80%" }, + ].map((pos, index) => ( + {`Cloud + ))} +
+ + {/* Word Hunt (Sound Match) - Listen to Sound and choose the right word */} + {isSoundMatch && !isPictureWords && currentQuestion?.allwords && ( + <> + + +
+ {currentQuestion?.allwords.map((item, index) => { + const isSelected = selectedWord === item.text; + const isCorrect = + item.text?.toLowerCase() === + currentQuestion.correctWord?.toLowerCase(); + const showCorrect = isSelected && isCorrect; + const showWrong = isSelected && !isCorrect; + return ( +
{ + if (isAudioPlayedOnce && !hasSelectedOption) { + handleWordClick(item.text); + } + }} + > + + {item.text} + +
+ ); + })} +
+ + )} + + {/* Sound Hunt (Picture words) - Read the word and choose the right sound */} + {!isSoundMatch && + isPictureWords && + currentQuestion?.word && + currentQuestion?.audioOptions && + Array.isArray(currentQuestion.audioOptions) && + currentQuestion.audioOptions.length > 0 && ( + <> + {/* Display the word */} +
+ + {currentQuestion.word} + +
+ + {/* Audio options */} +
+ {currentQuestion?.audioOptions.map((audioOption, index) => { + const isPlaying = playingAudioIndex === index; + const isSelected = selectedAudioIndex === index; + const isCorrect = audioOption.isCorrect; + const isWrong = wrongAudioIndex === index; + + // Determine background color and border based on state + let backgroundColor = "#FFFFFF"; + let borderColor = "2px solid rgba(255, 255, 255, 0.5)"; + + if (isSelected && isCorrect) { + backgroundColor = "#E8F5E9"; + borderColor = "3px solid #4CAF50"; + } else if (isSelected && !isCorrect) { + backgroundColor = "#FFEBEE"; + borderColor = "3px solid #F44336"; + } else if (isWrong) { + backgroundColor = "#FFEBEE"; + borderColor = "3px solid #F44336"; + } else if (isSelected) { + backgroundColor = "#E3F2FD"; + borderColor = "3px solid #2196F3"; + } + + return ( +
{ + if (!hasSelectedOption) { + handleAudioClick(index); + } + }} + > + {/* Checkbox indicator - shown when selected */} + {isSelected && ( +
+ {isCorrect ? ( + + ✓ + + ) : ( + + ✕ + + )} +
+ )} + + {/* Unselected checkbox indicator - shown when not selected and no option has been selected yet */} + {!isSelected && !hasSelectedOption && ( +
+ {/* Empty checkbox */} +
+ )} + + + + Sound {index + 1} + +
+ ); + })} +
+ + )} + + {/* Next Button - hidden since we auto-advance after selection */} + {false && hasSelectedOption && recording === "no" && ( +
+ { + console.log("Next button clicked"); + // Note: This button should not be visible since we auto-advance after selection + // But if somehow clicked, just move to next question + if (currentQuestionIndex < filteredContent.length - 1) { + setCurrentQuestionIndex((prevIndex) => prevIndex + 1); + setSelectedWord(null); + setSelectedAudioIndex(null); + setHasSelectedOption(false); + setIsAudioPlayedOnce(false); + setIsPlaying(false); + setPlayingAudioIndex(null); + } + // Completion is handled automatically in handleWordClick/handleAudioClick + }} + > + + +
+ )} + + )} + + {recording === "recording" && ( +
+
+ + {isSoundMatch + ? currentQuestion.correctWord + : currentQuestion.word} + +
+ { + await startRecording(); + setRecording("startRec"); + }} + src={Assets.pzMic} + alt="mic" + style={{ width: "70px", height: "70px", cursor: "pointer" }} + /> +
+ )} + + {recording === "startRec" && ( +
+
+ + {isSoundMatch + ? currentQuestion.correctWord + : currentQuestion.word} + +
+ + + + { + console.log("Stop button clicked"); + + // Stop recording + await stopRecording(); + + const audio = new Audio(correctSound); + audio.play(); + setRecording("no"); + setIsPlaying(false); + setIsAudioPlayedOnce(false); + setPlayingAudioIndex(null); + + // Note: Completion is handled automatically in handleWordClick/handleAudioClick + // This button should only stop recording - auto-advance handles completion + // If somehow on last question, completion logic in handleWordClick/handleAudioClick will handle it + }} + src={Assets.pause} + alt="Stop" + style={{ width: "60px", height: "60px", cursor: "pointer" }} + /> +
+ )} +
+
+ ); +}; + +export default SoundHuntS1Combined; diff --git a/src/assets/F1.png b/src/assets/F1.png new file mode 100644 index 00000000..bdf14a59 Binary files /dev/null and b/src/assets/F1.png differ diff --git a/src/assets/F2.png b/src/assets/F2.png new file mode 100644 index 00000000..c0822f08 Binary files /dev/null and b/src/assets/F2.png differ diff --git a/src/assets/F3.png b/src/assets/F3.png new file mode 100644 index 00000000..1c9cda8b Binary files /dev/null and b/src/assets/F3.png differ diff --git a/src/assets/R0m_template.png b/src/assets/R0m_template.png new file mode 100644 index 00000000..7e32af34 Binary files /dev/null and b/src/assets/R0m_template.png differ diff --git a/src/components/Assesment/AlphabetChart.jsx b/src/components/Assesment/AlphabetChart.jsx new file mode 100644 index 00000000..a03832fc --- /dev/null +++ b/src/components/Assesment/AlphabetChart.jsx @@ -0,0 +1,909 @@ +import React, { useState, useMemo, useEffect, useRef } from "react"; +import PropTypes from "prop-types"; +import { + Box, + Typography, + IconButton, + Button, + Grid, + Dialog, + ToggleButton, + ToggleButtonGroup, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import ArrowBackIosIcon from "@mui/icons-material/ArrowBackIos"; +import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos"; +import VolumeUpIcon from "@mui/icons-material/VolumeUp"; +import { + dataEn as letterDataEn, + dataHi as letterDataHi, + dataTe as letterDataTe, + dataKn as letterDataKn, +} from "../../RFlow/LetterTrain"; + +import { + wordData, + TeluguGunithas, + KannadaGunithas, +} from "../../RFlow/Barakhadi"; +import { getAssetAudioUrl, getAssetUrl } from "../../utils/rFlowS3Links"; +import { interact } from "../../services/telementryService"; +import { motion, AnimatePresence } from "framer-motion"; +import { getFontFamily } from "../../utils/fontUtils"; + +const TeluguGunithaCard = ({ item, playAudio, isActive }) => { + return ( + + playAudio(item)} + > + {/* Top Row — audio icon right-aligned */} + + { + e.stopPropagation(); + playAudio(item); + }} + > + + + + + {/* Image — flex:1 fills remaining space, contain shows full image */} + + {item.image && ( + {item.label + )} + + + + ); +}; + +const AlphabetCard = ({ item, playAudio, isActive, mode, lang }) => { + const renderHighlightedWord = () => { + const originalWord = item.word || ""; + const displayVal = item.display || ""; + + if (!isActive || !displayVal || !originalWord) { + return ( + originalWord.charAt(0).toUpperCase() + + originalWord.slice(1).toLowerCase() + ); + } + + // Manual capitalization for the whole word first + const capitalizedWord = + originalWord.charAt(0).toUpperCase() + + originalWord.slice(1).toLowerCase(); + const lowerWord = capitalizedWord.toLowerCase(); + const lowerHighlight = displayVal.toLowerCase(); + + const index = lowerWord.indexOf(lowerHighlight); + if (index === -1) return capitalizedWord; + + const before = capitalizedWord.substring(0, index); + const middle = capitalizedWord.substring(index, index + displayVal.length); + const after = capitalizedWord.substring(index + displayVal.length); + + return ( + <> + {before} + + {middle} + + {after} + + ); + }; + + return ( + + { + if (mode === "alphabet" && item.alaphabetChartAudio) { + playAudio(item, item.alaphabetChartAudio); + } else { + playAudio(item); + } + }} + > + {/* Top Row */} + + + {item.display} + + + { + e.stopPropagation(); + if (mode === "alphabet" && item.alaphabetChartAudio) { + playAudio(item, item.alaphabetChartAudio); + } else { + playAudio(item); + } + }} + > + + + + + {/* Image */} + + {item.image && ( + {item.word} + )} + + + {/* Word */} + + {renderHighlightedWord()} + + + + ); +}; + +const TELUGU_ORDER = [ + "అ", + "ఆ", + "ఇ", + "ఈ", + "ఉ", + "ఊ", + "ఋ", + "ౠ", + "ఎ", + "ఏ", + "ఐ", + "ఒ", + "ఓ", + "ఔ", + "అం", + "అః", + "క", + "ఖ", + "గ", + "ఘ", + "ఙ", + "చ", + "ఛ", + "జ", + "ఝ", + "ఞ", + "ట", + "ఠ", + "డ", + "ఢ", + "ణ", + "త", + "థ", + "ద", + "ధ", + "న", + "ప", + "ఫ", + "బ", + "భ", + "మ", + "య", + "ర", + "ల", + "వ", + "శ", + "ష", + "స", + "హ", + "ళ", + "క్ష", + "ఱ", +]; + +const KANNADA_ORDER = [ + "ಅ", + "ಆ", + "ಇ", + "ಈ", + "ಉ", + "ಊ", + "ಋ", + "ಎ", + "ಏ", + "ಐ", + "ಒ", + "ಓ", + "ಔ", + "ಅಂ", + "ಅಃ", + "ಕ", + "ಖ", + "ಗ", + "ಘ", + "ಙ", + "ಚ", + "ಛ", + "ಜ", + "ಝ", + "ಞ", + "ಟ", + "ಠ", + "ಡ", + "ಢ", + "ಣ", + "ತ", + "ಥ", + "ದ", + "ಧ", + "ನ", + "ಪ", + "ಫ", + "ಬ", + "ಭ", + "ಮ", + "ಯ", + "ರ", + "ಲ", + "ವ", + "ಶ", + "ಷ", + "ಸ", + "ಹ", + "ಳ", + "ಕ್ಷ", + "ಜ್ಞ", +]; + +export const TELUGU_ORDER_MAP = TELUGU_ORDER.reduce((acc, letter, index) => { + acc[letter] = index; + return acc; +}, {}); + +export const KANNADA_ORDER_MAP = KANNADA_ORDER.reduce((acc, letter, index) => { + acc[letter] = index; + return acc; +}, {}); + +const AlphabetChart = ({ open, onClose, lang }) => { + const [currentPage, setCurrentPage] = useState(0); + const [playingKey, setPlayingKey] = useState(null); + const [viewMode, setViewMode] = useState("alphabet"); // "alphabet" | "word" + const [activeCardKey, setActiveCardKey] = useState(null); + + const audioRef = useRef(null); + const currentSrcRef = useRef(null); + + // Normalize lang to ensure it's at least undefined (safe for localeCompare) and not null + const activeLang = lang || "en"; + + // Stop audio on unmount + useEffect(() => { + return () => stopAudio(); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + // Stop audio when dialog is closed + useEffect(() => { + if (!open) stopAudio(); + }, [open]); // eslint-disable-line react-hooks/exhaustive-deps + + const itemsPerPage = 8; + + const rawData = useMemo(() => { + if (lang === "hi") return letterDataHi; + if (lang === "te") return letterDataTe; + if (lang === "kn") return letterDataKn; + if (lang === "en") return letterDataEn; + return []; // Return empty array for unsupported languages (like "ta") + }, [lang]); + + const alphabetItems = useMemo(() => { + return rawData + .filter((group) => "letter" in group && group.letter) + .map((group) => { + const first = group.items?.[0] || {}; + + return { + key: group.letter, + display: group.letter, + word: first.word || "", + image: first.image || "", + audio: first.singleAudio || first.audio || "", + alaphabetChartAudio: first.alaphabetChartAudio || "", + }; + }) + .filter((item) => item.display && item.word && item.audio && item.image) + .sort((a, b) => { + if (activeLang === "te") { + const aIndex = TELUGU_ORDER_MAP[a.display] ?? Number.MAX_SAFE_INTEGER; + const bIndex = TELUGU_ORDER_MAP[b.display] ?? Number.MAX_SAFE_INTEGER; + return aIndex - bIndex; + } + if (activeLang === "kn") { + const aIndex = + KANNADA_ORDER_MAP[a.display] ?? Number.MAX_SAFE_INTEGER; + const bIndex = + KANNADA_ORDER_MAP[b.display] ?? Number.MAX_SAFE_INTEGER; + return aIndex - bIndex; + } + + // default for other languages + return (a.display || "").localeCompare(b.display || "", activeLang); + }); + }, [rawData, activeLang]); + + const wordItems = useMemo(() => { + let gunithaSource = null; + if (activeLang === "te") { + gunithaSource = TeluguGunithas; + } else if (activeLang === "kn") { + gunithaSource = KannadaGunithas; + } + + const gunithaItems = gunithaSource + ? gunithaSource + .map((g, idx) => ({ + key: `${activeLang}-gunitha-${idx}`, + display: "", + word: "", + label: g.audio ? g.audio.replace(/\.wav$/i, "") : "", + image: getAssetUrl(g.image) || "", + audio: g.audio ? getAssetAudioUrl(g.audio) : "", + isGunitha: true, + })) + .filter((item) => item.image && item.audio) + : []; + + if (activeLang !== "en" && wordData[activeLang]) { + const syllableItems = wordData[activeLang] + .map((itm, idx) => ({ + key: `${activeLang}-${idx}`, + display: itm.text, + word: itm.text, + image: getAssetUrl(itm.image) || "", + audio: (itm.audio ? getAssetAudioUrl(itm.audio) : "") || "", + })) + .filter( + (item) => item.display && item.word && item.audio && item.image + ); + return [...gunithaItems, ...syllableItems]; + } + + const syllableItems = rawData + .filter((group) => "syllable" in group && group.syllable) + .map((group) => { + const first = group.items?.[0] || {}; + + return { + key: group.syllable, + display: group.syllable, + word: first.word || "", + image: first.image || "", + audio: first.audio || first.singleAudio || "", + }; + }) + .filter((item) => item.display && item.word && item.audio && item.image); + + return [...gunithaItems, ...syllableItems]; + }, [rawData, activeLang]); + + const data = viewMode === "alphabet" ? alphabetItems : wordItems; + + const totalPages = Math.ceil(data.length / itemsPerPage); + const currentItems = data.slice( + currentPage * itemsPerPage, + (currentPage + 1) * itemsPerPage + ); + + const stopAudio = () => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + audioRef.current = null; + } + currentSrcRef.current = null; + setPlayingKey(null); + setActiveCardKey(null); + }; + + const handleNext = () => { + if (currentPage < totalPages - 1) { + interact( + "ET", + `Page Navigate : Next (Page ${currentPage + 2})`, + "alphabet-chart" + ); + stopAudio(); + setCurrentPage((prev) => prev + 1); + } + }; + + const handlePrev = () => { + if (currentPage > 0) { + interact( + "ET", + `Page Navigate : Previous (Page ${currentPage})`, + "alphabet-chart" + ); + stopAudio(); + setCurrentPage((prev) => prev - 1); + } + }; + + const playAudio = (item, specificAudio = null) => { + const audioSrc = specificAudio || item.audio; + if (!audioSrc) return; + + // If same source is already playing, do nothing + if (playingKey === item.key && currentSrcRef.current === audioSrc) { + return; + } + + interact( + "ET", + `Card Click : ${viewMode === "alphabet" ? "Alphabet" : "Syllable"} - ${ + item.display || item.label || "" + }`, + "alphabet-chart" + ); + + // Stop previous audio + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + setActiveCardKey(item.key); + setPlayingKey(item.key); + currentSrcRef.current = audioSrc; + + const audio = new Audio(audioSrc); + audioRef.current = audio; + + audio.onended = () => { + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + audioRef.current = null; + }; + audio.onerror = () => { + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + audioRef.current = null; + }; + audio.play().catch(() => { + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + audioRef.current = null; + }); + }; + + const getToggleLabel = (type) => { + const labels = { + en: { alphabet: "Alphabet", syllable: "Syllable" }, + hi: { alphabet: "वर्णमाला", syllable: "मात्रा" }, + te: { alphabet: "అక్షరమాల", syllable: "గుణింతాలు" }, + kn: { alphabet: "ಅಕ್ಷರಮಾಲೆ", syllable: "ಗುಣಿತಾಕ್ಷರ" }, + ta: { alphabet: "எழுத்துக்கள்", syllable: "சொற்கள்" }, + }; + + return labels[lang]?.[type] || labels.en[type]; + }; + + useEffect(() => { + stopAudio(); + setCurrentPage(0); + }, [viewMode, activeLang]); // eslint-disable-line react-hooks/exhaustive-deps + + if (!open) return null; + + return ( + + {/* Header */} + + {/* CENTER — Toggle */} + { + if (newMode) { + interact( + "ET", + `View Toggle : ${ + newMode === "alphabet" ? "Alphabet" : "Syllable" + }`, + "alphabet-chart" + ); + setViewMode(newMode); + } + }} + aria-label="view mode" + sx={{ + "& .MuiToggleButton-root": { + px: { xs: 3, sm: 4 }, + py: { xs: 1, sm: 1.2 }, + fontSize: { xs: "1rem", sm: "1.1rem" }, + fontWeight: 600, + borderRadius: "12px !important", + textTransform: "none", + + // 👇 border for unselected + border: "2px solid #94a3b8", + color: "#334155", + backgroundColor: "#f8fafc", + + transition: "all 0.2s ease", + }, + + "& .Mui-selected": { + bgcolor: "#333F61 !important", + color: "#fff !important", + borderColor: "#333F61", + boxShadow: "0 3px 10px rgba(0,0,0,0.15)", + }, + + "& .MuiToggleButtonGroup-grouped": { + mx: 0.5, + }, + }} + > + + {getToggleLabel("alphabet")} + + + + {getToggleLabel("syllable")} + + + + {/* RIGHT — Close */} + + { + interact("ET", "Close Alphabet Chart", "alphabet-chart"); + onClose(); + }} + size="medium" + aria-label="Close" + sx={{ + color: "#111827", + bgcolor: "rgba(255,255,255,0.7)", + borderRadius: "50%", + "&:hover": { + bgcolor: "#f3f4f6", + }, + }} + > + + + + + + {/* Content */} + + {data.length === 0 ? ( + + + + No Data Found + + + Sorry, we don't have any{" "} + {viewMode === "alphabet" ? "alphabets" : "syllables"} for this + language yet. + + + + ) : ( + + + {currentItems.map((item, index) => ( + + + {item.isGunitha ? ( + + ) : ( + + )} + + + ))} + + + )} + + + {/* Footer */} + + + + + + Page {currentPage + 1} of {totalPages || 1} + + + + + + ); +}; + +AlphabetChart.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + lang: PropTypes.string.isRequired, +}; + +export default AlphabetChart; diff --git a/src/components/Assesment/AlphabetChartPreview.jsx b/src/components/Assesment/AlphabetChartPreview.jsx new file mode 100644 index 00000000..835c3225 --- /dev/null +++ b/src/components/Assesment/AlphabetChartPreview.jsx @@ -0,0 +1,1644 @@ +import React, { useState, useEffect, useRef, useMemo } from "react"; +import { + Box, + Typography, + IconButton, + Button, + Grid, + Dialog, + ToggleButton, + ToggleButtonGroup, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import VolumeUpIcon from "@mui/icons-material/VolumeUp"; +import ReplayIcon from "@mui/icons-material/Replay"; +import SportsEsportsIcon from "@mui/icons-material/SportsEsports"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import TouchAppIcon from "@mui/icons-material/TouchApp"; +import { + dataEn as letterDataEn, + dataHi as letterDataHi, + dataTe as letterDataTe, + dataKn as letterDataKn, +} from "../../RFlow/LetterTrain"; +import { wordData } from "../../RFlow/Barakhadi"; +import { getAssetAudioUrl, getAssetUrl } from "../../utils/rFlowS3Links"; +import { TELUGU_ORDER_MAP, KANNADA_ORDER_MAP } from "./AlphabetChart"; +import { motion, AnimatePresence } from "framer-motion"; +import { + playTTS, + stopAllAudio, +} from "../../lib/axl-explorations/src/utils/audioUtils"; + +// Demo instructions for each language +const demoInstructions = { + en: { + title: "Alphabet Chart", + description: "Learn letters and words with sounds!", + howToPlay: "How to Use", + alphabetLabel: "Alphabet", + syllableLabel: "Syllable", + steps: ["Learn Alphabets", "Learn Syllables", "Complete!"], + // Alphabet phase instructions + alphabetInstruction1: "Click on any alphabet card to hear its sound", + alphabetInstruction2: "Great! Keep clicking on more alphabet cards", + alphabetInstruction3: + "Excellent! Now let's learn syllables. Click the Syllable toggle above", + // Syllable phase instructions + syllableInstruction1: "Now click on any syllable card to hear its sound", + syllableInstruction2: "Great! Keep clicking on more syllable cards", + syllableInstruction3: + "Excellent! You've learned how to use the Alphabet Chart!", + // Narrations + alphabetNarration1: "Click on any alphabet card to hear its sound", + alphabetNarration2: "Great! Keep clicking on more alphabet cards", + alphabetNarration3: + "Excellent! Now let's learn syllables. Click the Syllable toggle above", + syllableNarration1: "Now click on any syllable card to hear its sound", + syllableNarration2: "Great! Keep clicking on more syllable cards", + syllableNarration3: + "Excellent! You've learned how to use the Alphabet Chart!", + noCardsMessage: "No cards available for this section.", + startButton: "Start Exploring", + skipDemo: "Skip Demo", + replayDemo: "Replay Demo", + completion: { + title: "Demo Complete!", + description: "You're ready to explore the Alphabet Chart!", + }, + }, + te: { + title: "అక్షరమాల చార్ట్", + description: "శబ్దాలతో అక్షరాలు మరియు పదాలు నేర్చుకోండి!", + howToPlay: "ఎలా ఉపయోగించాలి", + alphabetLabel: "అక్షరమాల", + syllableLabel: "గుణింతాలు", + steps: ["అక్షరాలు నేర్చుకోండి", "గుణింతాలు నేర్చుకోండి", "పూర్తయింది!"], + alphabetInstruction1: "శబ్దం వినడానికి ఏదైనా అక్షర కార్డ్‌పై క్లిక్ చేయండి", + alphabetInstruction2: "బాగుంది! మరిన్ని అక్షర కార్డ్స్‌పై క్లిక్ చేయండి", + alphabetInstruction3: + "అద్భుతం! ఇప్పుడు గుణింతాలు నేర్చుకుందాం. పైన గుణింతాలు టాగుల్ క్లిక్ చేయండి", + syllableInstruction1: + "ఇప్పుడు శబ్దం వినడానికి ఏదైనా గుణింత కార్డ్‌పై క్లిక్ చేయండి", + syllableInstruction2: "బాగుంది! మరిన్ని గుణింత కార్డ్స్‌పై క్లిక్ చేయండి", + syllableInstruction3: + "అద్భుతం! అక్షరమాల చార్ట్‌ను ఎలా ఉపయోగించాలో నేర్చుకున్నారు!", + alphabetNarration1: "శబ్దం వినడానికి ఏదైనా అక్షర కార్డ్‌పై క్లిక్ చేయండి", + alphabetNarration2: "బాగుంది! మరిన్ని అక్షర కార్డ్స్‌పై క్లిక్ చేయండి", + alphabetNarration3: + "అద్భుతం! ఇప్పుడు గుణింతాలు నేర్చుకుందాం. పైన గుణింతాలు టాగుల్ క్లిక్ చేయండి", + syllableNarration1: + "ఇప్పుడు శబ్దం వినడానికి ఏదైనా గుణింత కార్డ్‌పై క్లిక్ చేయండి", + syllableNarration2: "బాగుంది! మరిన్ని గుణింత కార్డ్స్‌పై క్లిక్ చేయండి", + syllableNarration3: + "అద్భుతం! అక్షరమాల చార్ట్‌ను ఎలా ఉపయోగించాలో నేర్చుకున్నారు!", + noCardsMessage: "ఈ విభాగంలో కార్డ్‌లు అందుబాటులో లేవు.", + startButton: "అన్వేషించడం ప్రారంభించండి", + skipDemo: "డెమో స్కిప్ చేయండి", + replayDemo: "డెమోను మళ్లీ ఆడండి", + completion: { + title: "డెమో పూర్తయింది!", + description: "అక్షరమాల చార్ట్‌ను అన్వేషించడానికి మీరు సిద్ధంగా ఉన్నారు!", + }, + }, + kn: { + title: "ವರ್ಣಮಾಲೆ ಚಾರ್ಟ್‌", + description: "ಧ್ವನಿಗಳೊಂದಿಗೆ ಅಕ್ಷರಗಳು ಮತ್ತು ಪದಗಳನ್ನು ಕಲಿಯಿರಿ!", + howToPlay: "ಹೇಗೆ ಬಳಸುವುದು", + alphabetLabel: "ಅಕ್ಷರಮಾಲೆ", + syllableLabel: "ಗುಣಿತಾಕ್ಷರ", + steps: ["ಅಕ್ಷರಗಳನ್ನು ಕಲಿಯಿರಿ", "ಗುಣಿತಾಕ್ಷರಗಳನ್ನು ಕಲಿಯಿರಿ", "ಪೂರ್ಣಗೊಂಡಿದೆ!"], + alphabetInstruction1: + "ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಯಾವುದೇ ಅಕ್ಷರ ಕಾರ್ಡ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + alphabetInstruction2: "ಅದ್ಭುತ! ಇನ್ನಷ್ಟು ಅಕ್ಷರ ಕಾರ್ಡ್‌ಗಳ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + alphabetInstruction3: + "ಅತ್ಯುತ್ತಮ! ಈಗ ಗುಣಿತಾಕ್ಷರಗಳನ್ನು ಕಲಿಯೋಣ. ಮೇಲೆ ಗುಣಿತಾಕ್ಷರ ಟಾಗಲ್ ಕ್ಲಿಕ್ ಮಾಡಿ", + syllableInstruction1: + "ಈಗ ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಯಾವುದೇ ಗುಣಿತಾಕ್ಷರ ಕಾರ್ಡ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + syllableInstruction2: + "ಅದ್ಭುತ! ಇನ್ನಷ್ಟು ಗುಣಿತಾಕ್ಷರ ಕಾರ್ಡ್‌ಗಳ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + syllableInstruction3: + "ಅತ್ಯುತ್ತಮ! ಅಕ್ಷರಮಾಲೆ ಚಾರ್ಟ್ ಅನ್ನು ಹೇಗೆ ಬಳಸುವುದು ಎಂದು ಕಲಿತಿದ್ದೀರಿ!", + alphabetNarration1: "ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಯಾವುದೇ ಅಕ್ಷರ ಕಾರ್ಡ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + alphabetNarration2: "ಅದ್ಭುತ! ಇನ್ನಷ್ಟು ಅಕ್ಷರ ಕಾರ್ಡ್‌ಗಳ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + alphabetNarration3: + "ಅತ್ಯುತ್ತಮ! ಈಗ ಗುಣಿತಾಕ್ಷರಗಳನ್ನು ಕಲಿಯೋಣ. ಮೇಲೆ ಗುಣಿತಾಕ್ಷರ ಟಾಗಲ್ ಕ್ಲಿಕ್ ಮಾಡಿ", + syllableNarration1: + "ಈಗ ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಯಾವುದೇ ಗುಣಿತಾಕ್ಷರ ಕಾರ್ಡ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + syllableNarration2: + "ಅದ್ಭುತ! ಇನ್ನಷ್ಟು ಗುಣಿತಾಕ್ಷರ ಕಾರ್ಡ್‌ಗಳ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + syllableNarration3: + "ಅತ್ಯುತ್ತಮ! ಅಕ್ಷರಮಾಲೆ ಚಾರ್ಟ್ ಅನ್ನು ಹೇಗೆ ಬಳಸುವುದು ಎಂದು ಕಲಿತಿದ್ದೀರಿ!", + noCardsMessage: "ಈ ವಿಭಾಗದಲ್ಲಿ ಯಾವುದೇ ಕಾರ್ಡ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ.", + startButton: "ಅನ್ವೇಷಿಸಲು ಪ್ರಾರಂಭಿಸಿ", + skipDemo: "ಡೆಮೊ ಸ್ಕಿಪ್ ಮಾಡಿ", + replayDemo: "ಡೆಮೊ ಮತ್ತೆ ಆಡಿ", + completion: { + title: "ಡೆಮೊ ಪೂರ್ಣಗೊಂಡಿದೆ!", + description: "ಅಕ್ಷರಮಾಲೆ ಚಾರ್ಟ್ ಅನ್ನು ಅನ್ವೇಷಿಸಲು ನೀವು ಸಿದ್ಧರಿದ್ದೀರಿ!", + }, + }, + hi: { + title: "वर्णमाला चार्ट", + description: "ध्वनियों के साथ अक्षर और शब्द सीखें!", + howToPlay: "कैसे उपयोग करें", + alphabetLabel: "वर्णमाला", + syllableLabel: "मात्रा", + steps: ["वर्णमाला सीखें", "मात्रा सीखें", "पूर्ण!"], + alphabetInstruction1: + "ध्वनि सुनने के लिए किसी भी अक्षर कार्ड पर क्लिक करें", + alphabetInstruction2: "बहुत अच्छा! और अक्षर कार्ड्स पर क्लिक करें", + alphabetInstruction3: + "उत्कृष्ट! अब मात्रा सीखते हैं। ऊपर मात्रा टॉगल पर क्लिक करें", + syllableInstruction1: + "अब ध्वनि सुनने के लिए किसी भी मात्रा कार्ड पर क्लिक करें", + syllableInstruction2: "बहुत अच्छा! और मात्रा कार्ड्स पर क्लिक करें", + syllableInstruction3: + "उत्कृष्ट! आपने वर्णमाला चार्ट का उपयोग करना सीख लिया!", + alphabetNarration1: "ध्वनि सुनने के लिए किसी भी अक्षर कार्ड पर क्लिक करें", + alphabetNarration2: "बहुत अच्छा! और अक्षर कार्ड्स पर क्लिक करें", + alphabetNarration3: + "उत्कृष्ट! अब मात्रा सीखते हैं। ऊपर मात्रा टॉगल पर क्लिक करें", + syllableNarration1: + "अब ध्वनि सुनने के लिए किसी भी मात्रा कार्ड पर क्लिक करें", + syllableNarration2: "बहुत अच्छा! और मात्रा कार्ड्स पर क्लिक करें", + syllableNarration3: "उत्कृष्ट! आपने वर्णमाला चार्ट का उपयोग करना सीख लिया!", + noCardsMessage: "इस अनुभाग में कोई कार्ड उपलब्ध नहीं है।", + startButton: "खोजना शुरू करें", + skipDemo: "डेमो छोड़ें", + replayDemo: "डेमो फिर से चलाएं", + completion: { + title: "डेमो पूर्ण!", + description: "आप वर्णमाला चार्ट खोजने के लिए तैयार हैं!", + }, + }, + mr: { + title: "अक्षर चार्ट", + description: "आवाजांसह अक्षरे आणि शब्द शिका!", + howToPlay: "कसे वापरायचे", + alphabetLabel: "अक्षरे", + syllableLabel: "मात्रा", + steps: ["अक्षरे शिका", "मात्रा शिका", "पूर्ण!"], + alphabetInstruction1: "आवाज ऐकण्यासाठी कोणत्याही अक्षर कार्डवर क्लिक करा", + alphabetInstruction2: "छान! आणखी अक्षर कार्डांवर क्लिक करा", + alphabetInstruction3: + "उत्कृष्ट! आता मात्रा शिकूया. वर मात्रा टॉगल क्लिक करा", + syllableInstruction1: + "आता आवाज ऐकण्यासाठी कोणत्याही मात्रा कार्डवर क्लिक करा", + syllableInstruction2: "छान! आणखी मात्रा कार्डांवर क्लिक करा", + syllableInstruction3: + "उत्कृष्ट! तुम्ही अक्षर चार्ट कसे वापरायचे ते शिकलात!", + alphabetNarration1: "आवाज ऐकण्यासाठी कोणत्याही अक्षर कार्डवर क्लिक करा", + alphabetNarration2: "छान! आणखी अक्षर कार्डांवर क्लिक करा", + alphabetNarration3: "उत्कृष्ट! आता मात्रा शिकूया. वर मात्रा टॉगल क्लिक करा", + syllableNarration1: + "आता आवाज ऐकण्यासाठी कोणत्याही मात्रा कार्डवर क्लिक करा", + syllableNarration2: "छान! आणखी मात्रा कार्डांवर क्लिक करा", + syllableNarration3: "उत्कृष्ट! तुम्ही अक्षर चार्ट कसे वापरायचे ते शिकलात!", + noCardsMessage: "या विभागात कोणतेही कार्ड उपलब्ध नाहीत.", + startButton: "शोधणे सुरू करा", + skipDemo: "डेमो वगळा", + replayDemo: "डेमो पुन्हा खेळा", + completion: { + title: "डेमो पूर्ण झाले!", + description: "तुम्ही अक्षर चार्ट शोधण्यास तयार आहात!", + }, + }, +}; + +// Demo Card Component for Preview +const DemoAlphabetCard = ({ + item, + playAudio, + isActive, + mode, + isHighlighted, + showHandPointer, +}) => { + const renderHighlightedWord = () => { + const originalWord = item.word || ""; + const displayVal = item.display || ""; + + if (!isActive || !displayVal || !originalWord) { + return ( + originalWord.charAt(0).toUpperCase() + + originalWord.slice(1).toLowerCase() + ); + } + + const capitalizedWord = + originalWord.charAt(0).toUpperCase() + + originalWord.slice(1).toLowerCase(); + const lowerWord = capitalizedWord.toLowerCase(); + const lowerHighlight = displayVal.toLowerCase(); + + const index = lowerWord.indexOf(lowerHighlight); + if (index === -1) return capitalizedWord; + + const before = capitalizedWord.substring(0, index); + const middle = capitalizedWord.substring(index, index + displayVal.length); + const after = capitalizedWord.substring(index + displayVal.length); + + return ( + <> + {before} + + {middle} + + {after} + + ); + }; + + return ( + + { + if (!showHandPointer) return; + if (mode === "alphabet" && item.alaphabetChartAudio) { + playAudio(item, item.alaphabetChartAudio); + } else { + playAudio(item); + } + }} + > + {/* Hand Pointer Animation */} + {showHandPointer && ( + + + + )} + + {/* Top Row */} + + + {item.display} + + + { + e.stopPropagation(); + if (!showHandPointer) return; + if (mode === "alphabet" && item.alaphabetChartAudio) { + playAudio(item, item.alaphabetChartAudio); + } else { + playAudio(item); + } + }} + disabled={!showHandPointer} + > + + + + + {/* Image */} + {item.image ? ( + + {item.word} + + ) : ( + + )} + + {/* Word */} + + {renderHighlightedWord()} + + + + ); +}; + +// Countdown Component +const CountdownDisplay = ({ count, onComplete }) => { + const [currentCount, setCurrentCount] = useState(count); + + useEffect(() => { + if (currentCount === 0) { + onComplete(); + return; + } + + const timer = setTimeout(() => { + setCurrentCount(currentCount - 1); + }, 1000); + + return () => clearTimeout(timer); + }, [currentCount, onComplete]); + + return ( + + + + + {currentCount} + + + + + {/* Progress dots */} + + {[3, 2, 1].map((num) => ( + + ))} + + + ); +}; + +// Completion Screen Component +const CompletionScreen = ({ lang, onStartExploring, onReplayDemo }) => { + const instructions = demoInstructions[lang] || demoInstructions.en; + + return ( + + + + + + + + + {instructions.completion.title} + + + + {instructions.completion.description} + + + + + + + + + ); +}; + +// Main AlphabetChartPreview Component +const AlphabetChartPreview = ({ open, onClose, lang, onStartExploring }) => { + const [previewPhase, setPreviewPhase] = useState("countdown"); // 'countdown' | 'demo' | 'completion' + const [viewMode, setViewMode] = useState("alphabet"); // 'alphabet' | 'word' + const [currentPage, setCurrentPage] = useState(0); + const [playingKey, setPlayingKey] = useState(null); + const [activeCardKey, setActiveCardKey] = useState(null); + const [highlightedCardIndex, setHighlightedCardIndex] = useState(0); + + // Track clicks for both phases + const [alphabetClickCount, setAlphabetClickCount] = useState(0); + const [syllableClickCount, setSyllableClickCount] = useState(0); + const [alphabetPhaseComplete, setAlphabetPhaseComplete] = useState(false); + const [waitingForToggle, setWaitingForToggle] = useState(false); + + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStepIndex, setCurrentStepIndex] = useState(0); + + const audioRef = useRef(null); + const currentSrcRef = useRef(null); + const narrationRef = useRef(null); + + const activeLang = lang || "en"; + const instructions = demoInstructions[activeLang] || demoInstructions.en; + const itemsPerPage = 4; + + // Stop audio on unmount + useEffect(() => { + return () => { + stopAllAudio(); + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current = null; + } + if (narrationRef.current) { + narrationRef.current.pause(); + narrationRef.current = null; + } + }; + }, []); + + // Reset state when opened + useEffect(() => { + if (open) { + setPreviewPhase("countdown"); + setViewMode("alphabet"); + setCurrentPage(0); + setAlphabetClickCount(0); + setSyllableClickCount(0); + setAlphabetPhaseComplete(false); + setWaitingForToggle(false); + setHighlightedCardIndex(0); + setCurrentStepIndex(0); + } + }, [open]); + + // Get data based on language + const rawData = useMemo(() => { + if (lang === "hi") return letterDataHi; + if (lang === "te") return letterDataTe; + if (lang === "kn") return letterDataKn; + if (lang === "en") return letterDataEn; + return []; + }, [lang]); + + const alphabetItems = useMemo(() => { + return rawData + .filter((group) => "letter" in group && group.letter) + .map((group) => { + const first = group.items?.[0] || {}; + return { + key: group.letter, + display: group.letter, + word: first.word || "", + image: first.image || "", + audio: first.singleAudio || first.audio || "", + alaphabetChartAudio: first.alaphabetChartAudio || "", + }; + }) + .sort((a, b) => { + if (activeLang === "te") { + const aIndex = TELUGU_ORDER_MAP[a.display] ?? Number.MAX_SAFE_INTEGER; + const bIndex = TELUGU_ORDER_MAP[b.display] ?? Number.MAX_SAFE_INTEGER; + return aIndex - bIndex; + } + if (activeLang === "kn") { + const aIndex = + KANNADA_ORDER_MAP[a.display] ?? Number.MAX_SAFE_INTEGER; + const bIndex = + KANNADA_ORDER_MAP[b.display] ?? Number.MAX_SAFE_INTEGER; + return aIndex - bIndex; + } + return (a.display || "").localeCompare(b.display || "", activeLang); + }); + }, [rawData, activeLang]); + + const wordItems = useMemo(() => { + if (activeLang !== "en" && wordData[activeLang]) { + return wordData[activeLang].map((itm, idx) => ({ + key: `${activeLang}-${idx}`, + display: itm.text, + word: itm.text, + image: getAssetUrl(itm.image) || "", + audio: (itm.audio ? getAssetAudioUrl(itm.audio) : "") || "", + })); + } + + return rawData + .filter((group) => "syllable" in group && group.syllable) + .map((group) => { + const first = group.items?.[0] || {}; + return { + key: group.syllable, + display: group.syllable, + word: first.word || "", + image: first.image || "", + audio: first.audio || first.singleAudio || "", + }; + }); + }, [rawData, activeLang]); + + const filteredAlphabetItems = useMemo( + () => + alphabetItems.filter( + (item) => item.display && item.word && item.audio && item.image + ), + [alphabetItems] + ); + + const filteredWordItems = useMemo( + () => + wordItems.filter( + (item) => item.display && item.word && item.audio && item.image + ), + [wordItems] + ); + + const data = + viewMode === "alphabet" ? filteredAlphabetItems : filteredWordItems; + const currentItems = data.slice( + currentPage * itemsPerPage, + (currentPage + 1) * itemsPerPage + ); + + // Get narration step key based on current state + const getNarrationStepKey = () => { + if (viewMode === "alphabet") { + if (waitingForToggle) { + return "alphabetNarration3"; + } else if (alphabetClickCount >= 2) { + return "alphabetNarration2"; + } else { + return "alphabetNarration1"; + } + } else { + if (syllableClickCount >= 3) { + return "syllableNarration3"; + } else if (syllableClickCount >= 1) { + return "syllableNarration2"; + } else { + return "syllableNarration1"; + } + } + }; + + // Play narration using audio files with TTS fallback + const playNarration = async (text, stepKey = null) => { + // Stop current narration if any + if (narrationRef.current) { + narrationRef.current.pause(); + narrationRef.current = null; + } + speechSynthesis.cancel(); + + setIsPlayingNarration(true); + // Get the step key if not provided + const narrationKey = stepKey || getNarrationStepKey(); + const audioPath = `/audio/audio-preview/Alphabet Chart/${activeLang}/${narrationKey}.wav`; + try { + // Try to play the audio file first + const audio = new Audio(audioPath); + narrationRef.current = audio; + + await new Promise((resolve, reject) => { + audio.onended = () => { + narrationRef.current = null; + resolve(); + }; + audio.onerror = () => { + narrationRef.current = null; + reject(new Error("Audio file not found")); + }; + audio.play().catch((e) => { + narrationRef.current = null; + reject(e); + }); + }); + } catch (error) { + // Fall back to TTS + try { + await playTTS(text, activeLang); + } catch (ttsError) { + console.warn("TTS also failed:", ttsError); + } + } finally { + setIsPlayingNarration(false); + } + }; + + const handleAlphabetModeProgress = () => { + const newCount = alphabetClickCount + 1; + setAlphabetClickCount(newCount); + const an = currentItems.length; + if (an > 0) setHighlightedCardIndex((prev) => (prev + 1) % an); + + if (newCount >= 3 && !alphabetPhaseComplete) { + // Alphabet phase complete, prompt to switch to syllable + setAlphabetPhaseComplete(true); + setWaitingForToggle(true); + setCurrentStepIndex(1); + } + }; + + const handleSyllableModeProgress = () => { + const newCount = syllableClickCount + 1; + setSyllableClickCount(newCount); + const wn = currentItems.length; + if (wn > 0) setHighlightedCardIndex((prev) => (prev + 1) % wn); + + if (newCount >= 3) { + // Demo complete — transition to completion after narration + setCurrentStepIndex(2); + setTimeout(() => { + setPreviewPhase("completion"); + }, 3000); + } + }; + + // Play card audio + const playAudio = (item, specificAudio = null) => { + const audioSrc = specificAudio || item.audio; + if (!audioSrc) return; + + // Don't allow clicks if waiting for toggle or if narration is playing + if (waitingForToggle || isPlayingNarration) return; + + if (playingKey === item.key && currentSrcRef.current === audioSrc) { + return; + } + + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + setActiveCardKey(item.key); + setPlayingKey(item.key); + currentSrcRef.current = audioSrc; + + const audio = new Audio(audioSrc); + audioRef.current = audio; + + audio.onended = () => { + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + audioRef.current = null; + + // Handle demo progression + if (previewPhase === "demo") { + if (viewMode === "alphabet") { + handleAlphabetModeProgress(); + } else { + handleSyllableModeProgress(); + } + } + }; + + audio.onerror = () => { + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + audioRef.current = null; + }; + + audio.play().catch(() => { + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + audioRef.current = null; + }); + }; + + // Handle toggle change + const handleToggleChange = (_, newMode) => { + if (!newMode) return; + + // Block switching to syllable if it's not the right time or narration is playing + if (newMode === "word" && (!waitingForToggle || isPlayingNarration)) { + return; + } + + // Block switching back to alphabet once syllable phase has started + if (newMode === "alphabet" && viewMode === "word") { + return; + } + + // Stop ongoing narration if any + if (narrationRef.current) { + narrationRef.current.pause(); + narrationRef.current = null; + } + speechSynthesis.cancel(); + setIsPlayingNarration(false); + + if ( + newMode === "word" && + waitingForToggle && + filteredWordItems.length === 0 + ) { + setWaitingForToggle(false); + setCurrentStepIndex(2); + setPreviewPhase("completion"); + return; + } + + setViewMode(newMode); + setCurrentPage(0); + setHighlightedCardIndex(0); + + if (newMode === "word" && waitingForToggle) { + setWaitingForToggle(false); + } + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + // If no data at all, skip demo entirely + if (filteredAlphabetItems.length === 0 && filteredWordItems.length === 0) { + stopAllLocalAudio(); + onStartExploring(); + return; + } + // If no alphabet data, jump straight to syllable phase + if (filteredAlphabetItems.length === 0) { + setAlphabetPhaseComplete(true); + setViewMode("word"); + setCurrentPage(0); + setHighlightedCardIndex(0); + setCurrentStepIndex(1); + } + setPreviewPhase("demo"); + }; + + const stopAllLocalAudio = () => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + audioRef.current = null; + } + if (narrationRef.current) { + narrationRef.current.pause(); + narrationRef.current = null; + } + speechSynthesis.cancel(); + setIsPlayingNarration(false); + setPlayingKey(null); + setActiveCardKey(null); + currentSrcRef.current = null; + stopAllAudio(); + }; + + const handleClose = () => { + stopAllLocalAudio(); + onClose(); + }; + + const handleStartExploring = () => { + stopAllLocalAudio(); + onStartExploring(); + }; + + // Handle skip demo + const handleSkipDemo = () => { + stopAllLocalAudio(); + onStartExploring(); + }; + + // Handle replay demo + const handleReplayDemo = () => { + stopAllLocalAudio(); + setPreviewPhase("countdown"); + setViewMode("alphabet"); + setAlphabetClickCount(0); + setSyllableClickCount(0); + setAlphabetPhaseComplete(false); + setWaitingForToggle(false); + setHighlightedCardIndex(0); + setCurrentStepIndex(0); + }; + + // Get current instruction text + const getCurrentInstruction = () => { + if (viewMode === "alphabet") { + if (waitingForToggle) { + return instructions.alphabetInstruction3; + } else if (alphabetClickCount >= 2) { + return instructions.alphabetInstruction2; + } else { + return instructions.alphabetInstruction1; + } + } else { + if (syllableClickCount >= 3) { + return instructions.syllableInstruction3; + } else if (syllableClickCount >= 1) { + return instructions.syllableInstruction2; + } else { + return instructions.syllableInstruction1; + } + } + }; + + // Get current narration text (for TTS) + const getCurrentNarration = () => { + if (viewMode === "alphabet") { + if (waitingForToggle) { + return instructions.alphabetNarration3; + } else if (alphabetClickCount >= 2) { + return instructions.alphabetNarration2; + } else { + return instructions.alphabetNarration1; + } + } else { + if (syllableClickCount >= 3) { + return instructions.syllableNarration3; + } else if (syllableClickCount >= 1) { + return instructions.syllableNarration2; + } else { + return instructions.syllableNarration1; + } + } + }; + + // Track the last played narration to avoid duplicate plays + const lastPlayedNarrationRef = useRef(null); + + // Auto-play TTS when instruction changes during demo phase + useEffect(() => { + if (!open || previewPhase !== "demo") { + stopAllLocalAudio(); + lastPlayedNarrationRef.current = null; + return; + } + + const currentNarration = getCurrentNarration(); + // Only play if this is a new narration text + if ( + currentNarration && + currentNarration !== lastPlayedNarrationRef.current + ) { + lastPlayedNarrationRef.current = currentNarration; + + // Small delay to ensure UI has updated + const timer = setTimeout(() => { + playNarration(currentNarration); + }, 300); + + return () => { + clearTimeout(timer); + }; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + open, + previewPhase, + viewMode, + alphabetClickCount, + syllableClickCount, + waitingForToggle, + ]); + + if (!open) return null; + + return ( + + {/* Header */} + + {/* Toggle in Header */} + {previewPhase === "demo" && ( + + + {instructions.alphabetLabel} + + = 3 + ? { + content: '"✓"', + position: "absolute", + top: -8, + right: -8, + bgcolor: "#22c55e", + color: "white", + borderRadius: "50%", + width: 24, + height: 24, + display: "flex", + alignItems: "center", + justifyContent: "center", + fontSize: "14px", + fontWeight: "bold", + } + : {}, + }} + > + {instructions.syllableLabel} + {/* Finger pointer for Syllable toggle - show after narration finishes */} + {waitingForToggle && ( + + + 👆 + + + )} + + + )} + + {/* Title for countdown */} + {previewPhase === "countdown" && ( + + {instructions.title} - {instructions.howToPlay} + + )} + + {/* Close Button */} + + + + + + + + {/* Content */} + + {/* Countdown Phase */} + {previewPhase === "countdown" && ( + + )} + + {/* Demo Phase */} + {previewPhase === "demo" && ( + + {/* Progress Steps */} + + {instructions.steps.map((step, index) => ( + + + + {index < currentStepIndex ? "✓" : index + 1} + + + {step} + + + {index < instructions.steps.length - 1 && ( + + )} + + ))} + + + {/* Instruction Text */} + + + + {getCurrentInstruction()} + + + + + {/* Demo Cards Grid */} + {currentItems.length === 0 ? ( + + + {instructions.noCardsMessage} + + + + ) : ( + + + {currentItems.slice(0, 4).map((item, index) => ( + + + + + + ))} + + + )} + + {/* Click counters */} + + + + {instructions.alphabetLabel} + + + {[1, 2, 3].map((num) => ( + = num ? "#22c55e" : "#d1d5db", + transition: "all 0.3s ease", + }} + /> + ))} + + + + + {instructions.syllableLabel} + + + {[1, 2, 3].map((num) => ( + = num ? "#22c55e" : "#d1d5db", + transition: "all 0.3s ease", + }} + /> + ))} + + + + + )} + + {/* Completion Phase */} + {previewPhase === "completion" && ( + + )} + + + {/* Footer - Only show in demo phase */} + {previewPhase === "demo" && ( + + + + + + )} + + ); +}; + +export default AlphabetChartPreview; diff --git a/src/components/Assesment/Assesment.jsx b/src/components/Assesment/Assesment.jsx index 65d6aa98..33817e46 100644 --- a/src/components/Assesment/Assesment.jsx +++ b/src/components/Assesment/Assesment.jsx @@ -13,12 +13,15 @@ import { List, ListItem, ListItemButton, + Button, ListItemText, Divider, } from "../../../node_modules/@mui/material/index"; import MenuIcon from "@mui/icons-material/Menu"; import LogoutIcon from "@mui/icons-material/Logout"; import TranslateIcon from "@mui/icons-material/Translate"; +import MicIcon from "@mui/icons-material/Mic"; +import MenuBookIcon from "@mui/icons-material/MenuBook"; import { useMediaQuery, useTheme } from "@mui/material"; import LogoutImg from "../../assets/images/logout.svg"; import { styled } from "@mui/material/styles"; @@ -31,7 +34,9 @@ import { languages, levelConfig, setLocalData, + getLanguageOrDefault, } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import practicebg from "../../assets/images/practice-bg.svg"; import { useNavigate } from "../../../node_modules/react-router-dom/dist/index"; import { useEffect, useState } from "react"; @@ -82,7 +87,7 @@ import config from "../../utils/urlConstants.json"; import panda from "../../assets/images/panda.svg"; import cryPanda from "../../assets/images/cryPanda.svg"; import { uniqueId } from "../../services/utilService"; -import { end } from "../../services/telementryService"; +import { end, interact } from "../../services/telementryService"; import { levelMapping } from "../../utils/levelData"; import scoreView from "../../assets/images/scoreView.svg"; import { @@ -94,6 +99,14 @@ import { getFetchMilestoneDetails } from "../../services/learnerAi/learnerAiServ import * as Assets from "../../utils/imageAudioLinks"; import NumberFlow from "@number-flow/react"; import LanguageModalNew from "../../utils/LanguageModal"; +import { AudioDiagnosticModal } from "../AudioDiagnostic"; +import { getF1FlowStep } from "../../RFlow/F1"; +import { getF2FlowStep } from "../../RFlow/F2"; +import { getF3FlowStep } from "../../RFlow/F3"; +import AlphabetChart from "./AlphabetChart"; +import AlphabetChartPreview from "./AlphabetChartPreview"; +import Backdrop from "@mui/material/Backdrop"; +import CircularProgress from "@mui/material/CircularProgress"; const theme = createTheme(); @@ -287,7 +300,7 @@ export const MessageDialog = ({ top: 0, left: 0, background: "rgba(0, 0, 0, 0.5)", - zIndex: 9999, + zIndex: 999999, }} > ( + +))({ + [`& .MuiTooltip-tooltip`]: { + fontSize: "1.2rem", // Adjust the font size as needed + }, +}); + export const ProfileHeader = ({ level, setOpenLangModal, @@ -406,7 +445,50 @@ export const ProfileHeader = ({ wordCount = 0, }) => { const language = lang || getLocalData("lang"); - const username = profileName || getLocalData("profileName"); + let username = profileName || getLocalData("profileName"); + + // Check if F2 flow is active and update username display + const getMilestoneData = () => { + try { + const milestoneStr = getLocalData("getMilestone"); + if (milestoneStr) { + return JSON.parse(milestoneStr); + } + } catch (e) { + console.error("Error parsing getMilestone:", e); + } + return null; + }; + const milestoneData = getMilestoneData(); + const milestoneLevel = milestoneData?.data?.milestone_level || null; + const subMilestoneLevel = milestoneData?.data?.sub_milestone_level || null; + const shouldShowF1 = milestoneLevel === "B" && subMilestoneLevel === "F1"; + const shouldShowF2 = milestoneLevel === "B" && subMilestoneLevel === "F2"; + const shouldShowF3 = milestoneLevel === "B" && subMilestoneLevel === "F3"; + const f2FlowStep = getF2FlowStep(); + const f3FlowStep = getF3FlowStep(); + const isF2FlowActive = shouldShowF2 && f2FlowStep.step !== null; + const isF3FlowActive = shouldShowF3 && f3FlowStep.step !== null; + + // If F3 flow is active and username contains "F1" or "F2", replace with "F3" + if ( + isF3FlowActive && + username && + typeof username === "string" && + (username.includes("F1") || username.includes("F2")) + ) { + username = username.replace(/F[12]/g, "F3"); + } + // If F2 flow is active and username contains "F1", replace with "F2" + else if ( + isF2FlowActive && + username && + typeof username === "string" && + username.includes("F1") + ) { + username = username.replace(/F1/g, "F2"); + } + const navigate = useNavigate(); const [openMessageDialog, setOpenMessageDialog] = useState(""); const theme = useTheme(); @@ -416,6 +498,12 @@ export const ProfileHeader = ({ const [animatedVocabCount, setAnimatedVocabCount] = useState(0); const [animatedWordCount, setAnimatedWordCount] = useState(0); const [milestone, setMilestone] = useState(0); + const [showAudioDiagnostic, setShowAudioDiagnostic] = useState(false); + const [openAlphabetModal, setOpenAlphabetModal] = useState(false); + const [openAlphabetPreview, setOpenAlphabetPreview] = useState(false); + const [showChartPointer, setShowChartPointer] = useState(false); + const chartAudioRef = React.useRef(null); + const [isAudioPlaying, setIsAudioPlaying] = useState(false); useEffect(() => { const rawMilestone = getLocalData("getMilestone"); @@ -429,7 +517,7 @@ export const ProfileHeader = ({ console.error("Failed to parse milestone data:", e); setMilestone(0); } - }, []); + }, [lang]); // Update when language changes to refresh milestone data useEffect(() => { const timeout = setTimeout(() => { @@ -447,6 +535,209 @@ export const ProfileHeader = ({ return () => clearTimeout(timeout); }, [wordCount]); + // Listen for demo completion to trigger chart pointer + + useEffect(() => { + const playChartAudio = () => { + // 🔒 Prevent double audio + if (chartAudioRef.current) { + chartAudioRef.current.pause(); + chartAudioRef.current.currentTime = 0; // rewind (good practice) + chartAudioRef.current = null; + } + + const audioPath = `/audio/audio-preview/Alphabet Chart/Chart Icon/${language}/ChartNarration.wav`; + const audio = new Audio(audioPath); + chartAudioRef.current = audio; + + audio.onplay = () => { + setShowChartPointer(true); + setIsAudioPlaying(true); + }; + + audio.onended = () => { + setShowChartPointer(false); // 👈 demo finished + setIsAudioPlaying(false); + chartAudioRef.current = null; + + // ✅ Audio done → now wait for user click + setLocalData("showAlphabetDemo", "false"); + }; + + audio.onerror = () => { + console.warn("Chart audio missing, showing pointer only"); + setShowChartPointer(false); + setIsAudioPlaying(false); + setLocalData("showAlphabetDemo", "false"); + chartAudioRef.current = null; + }; + + setTimeout(() => { + // 🛡️ Guard: if alphabetDemoStop cleared the ref, don't play + if (chartAudioRef.current !== audio) return; + audio.play().catch(() => { + setShowChartPointer(false); + setIsAudioPlaying(false); + setLocalData("showAlphabetDemo", "false"); + chartAudioRef.current = null; + }); + }, 500); + }; + // 🛡️ Initial mount check: validate F1 flow + milestone before playing + // This blocks stale showAlphabetDemo from a previous session + const checkDemoOnMount = () => { + const demoState = getLocalData("showAlphabetDemo"); + if (demoState !== "true") return; + + // Check if F1 flow is actually active + let isF1Active = false; + try { + const msStr = getLocalData("getMilestone"); + if (msStr) { + const msData = JSON.parse(msStr); + isF1Active = + msData?.data?.milestone_level === "B" && + msData?.data?.sub_milestone_level === "F1"; + } + } catch (e) { + // ignore parse errors + } + + if (isF1Active) { + // 🎯 Only auto-play for IMMEDIATE milestones (L1=index 0) + // Deferred milestones (A1=6, A2=13, A3=20) should ONLY play + // when user clicks "Start Game" → alphabetDemoTriggerRequest event + const immediateOnlyMilestones = [0]; + const rawIndex = getLocalData("f1FlowIndex"); + const currentF1Index = rawIndex !== null ? Number(rawIndex) : -1; + + // 🔒 Also check if this milestone was already played (prevents replay on re-login) + let playedIndices = []; + try { + const playedRaw = getLocalData("playedAlphabetDemoIndices"); + playedIndices = playedRaw ? JSON.parse(playedRaw) : []; + } catch (e) { + playedIndices = []; + } + + if ( + immediateOnlyMilestones.includes(currentF1Index) && + !playedIndices.includes(currentF1Index) + ) { + playChartAudio(); + return; + } + } + + // Not F1 flow or not at an allowed milestone → clear stale flag + setLocalData("showAlphabetDemo", "false"); + }; + + // 🎬 Event-based trigger: Practice.jsx already validated the milestone + // before dispatching alphabetDemoComplete, so just play + const handleDemoEvent = () => { + const demoState = getLocalData("showAlphabetDemo"); + if (demoState === "true") { + playChartAudio(); + } + }; + + // Initial check (validated) + checkDemoOnMount(); + + const handleStorageChange = (e) => { + if (e.key === "showAlphabetDemo" && e.newValue === "true") { + handleDemoEvent(); + } + }; + + window.addEventListener("storage", handleStorageChange); + window.addEventListener("alphabetDemoComplete", handleDemoEvent); + + // 🔇 Listen for stop signal from Practice.jsx (non-milestone cleanup) + const handleDemoStop = () => { + if (chartAudioRef.current) { + chartAudioRef.current.pause(); + chartAudioRef.current.currentTime = 0; + chartAudioRef.current = null; + } + setShowChartPointer(false); + setIsAudioPlaying(false); + setLocalData("showAlphabetDemo", "false"); + }; + window.addEventListener("alphabetDemoStop", handleDemoStop); + + return () => { + window.removeEventListener("storage", handleStorageChange); + window.removeEventListener("alphabetDemoComplete", handleDemoEvent); + window.removeEventListener("alphabetDemoStop", handleDemoStop); + + if (chartAudioRef.current) { + chartAudioRef.current.pause(); + chartAudioRef.current = null; + } + }; + }, [language]); + + const handleAlphabetChartOpen = () => { + // Check if demo was already completed (returns null first time → falsy) + const isDemoComplete = getLocalData("AlphabetDemoCompleted") === "true"; + interact("ET", "Open Alphabet Chart", "alphabet-chart"); + if (isDemoComplete) { + // 📘 User has seen demo before → show normal chart/modal + setOpenAlphabetModal(true); + } else { + // 🎬 First time → show demo/preview + setOpenAlphabetPreview(true); + + // Mark as completed so next time we skip demo + setLocalData("AlphabetDemoCompleted", "true"); + } + + // Common cleanup regardless of demo or normal mode + setShowChartPointer(false); + setIsAudioPlaying(false); + + if (chartAudioRef.current) { + chartAudioRef.current.pause(); + chartAudioRef.current = null; + } + }; + + useEffect(() => { + // Cleanup function – runs when component unmounts + // or when dependencies change (if you add any) + return () => { + if (chartAudioRef.current) { + chartAudioRef.current.pause(); + chartAudioRef.current.currentTime = 0; + chartAudioRef.current = null; + setShowChartPointer(false); // important if pointer is visual + setIsAudioPlaying(false); + } + }; + }, []); + + const getAlphabetTooltipText = () => { + const texts = { + en: { + title: "📚 Alphabet Chart", + desc: "If you need help with an alphabet or syllable, check the chart here.", + }, + te: { + title: "📚 అక్షరమాల చార్ట్", + desc: "మీకు ఏదైనా అక్షరం లేదా అక్షర సమూహంతో సహాయం కావాలా? అయితే, ఇక్కడ ఉన్న పట్టికను చూడండి.", + }, + kn: { + title: "📚 ವರ್ಣಮಾಲೆ ಚಾರ್ಟ್‌", + desc: "ನಿಮಗೆ ಅಕ್ಷರಗಳು ಅಥವಾ ಗುಣಿತಾಕ್ಷರಗಳನ್ನು ನೆನಪಿಸಿಕೊಳ್ಳಲು ಸಹಾಯ ಬೇಕಾದರೆ, ಇಲ್ಲಿರುವ ಚಾರ್ಟ್‌ ನೋಡಿ.", + }, + }; + + return texts[lang] || texts.en; + }; + const tooltipText = getAlphabetTooltipText(); + const handleProfileBack = () => { try { if (process.env.REACT_APP_IS_APP_IFRAME === "true") { @@ -476,32 +767,6 @@ export const ProfileHeader = ({ } }; - const CustomIconButton = styled(IconButton)({ - padding: "6px 20px", - color: "white", - fontSize: "20px", - fontWeight: 500, - borderRadius: "8px", - marginRight: "10px", - fontFamily: '"Lato", "sans-serif"', - position: "relative", - display: "flex", - alignItems: "center", - justifyContent: "center", - "& .logout-img": { - display: "block", - filter: "invert(1)", - }, - }); - - const CustomTooltip = styled(({ className, ...props }) => ( - - ))({ - [`& .MuiTooltip-tooltip`]: { - fontSize: "1.2rem", // Adjust the font size as needed - }, - }); - return ( <> {!!openMessageDialog && ( @@ -514,6 +779,10 @@ export const ProfileHeader = ({ dontShowHeader={openMessageDialog.dontShowHeader} /> )} + setShowAudioDiagnostic(false)} + /> + { + // Telemetry event for mobile menu click + interact( + "ET", + "Open Audio Diagnostic", + "header-mobile-menu" + ); + setShowAudioDiagnostic(true); + setMenuOpen(false); + }} + > + + + + {["B", "m1", "m2", "m3"].includes(milestoneLevel) && ( + <> + + + + + + + )} + + {["B", "m1", "m2", "m3"].includes(milestoneLevel) && ( + <> + theme.zIndex.drawer + 2, // higher than most things + }} + open={isAudioPlaying} + invisible={true} + /> + + + + + + {/* Animated finger pointer - only show when audio is playing */} + {showChartPointer && ( + + + 👆 + + + )} + + + {showChartPointer && ( + + + {/* Header */} + + + {tooltipText.title} + + + { + setShowChartPointer(false); + if (chartAudioRef.current) { + chartAudioRef.current.pause(); + chartAudioRef.current.currentTime = 0; + chartAudioRef.current = null; + } + setIsAudioPlaying(false); + setLocalData("showAlphabetDemo", "false"); + }} + > + + + + + {/* Description */} + + {tooltipText.desc} + + + + )} + + )} + {process.env.REACT_APP_IS_IN_APP_AUTHORISATION === "true" && ( setOpenLangModal ? setOpenLangModal(true) @@ -912,23 +1360,70 @@ export const ProfileHeader = ({ + {/* Audio Diagnostic Button - Subtle */} + + { + // Telemetry event for header button click + interact("ET", "Open Audio Diagnostic", "header"); + setShowAudioDiagnostic(true); + }} + sx={{ + mr: { xs: "5px", sm: "10px" }, + padding: isMobile ? "6px" : "8px", + backgroundColor: "rgba(255, 255, 255, 0.7)", + "&:hover": { + backgroundColor: "rgba(255, 255, 255, 0.9)", + }, + }} + > + + + {process.env.REACT_APP_IS_IN_APP_AUTHORISATION === "true" && ( - - - Logout - - + + Logout + )} )}
+ setOpenAlphabetPreview(false)} + lang={language} + onStartExploring={() => { + setOpenAlphabetPreview(false); + setOpenAlphabetModal(true); + }} + /> + setOpenAlphabetModal(false)} + lang={language} + /> ); }; @@ -941,6 +1436,7 @@ const Assesment = ({ discoverStart }) => { username = userDetails.student_name; setLocalData("profileName", username); } + // const [searchParams, setSearchParams] = useSearchParams(); // const [profileName, setProfileName] = useState(username); const [openMessageDialog, setOpenMessageDialog] = useState(""); @@ -948,10 +1444,11 @@ const Assesment = ({ discoverStart }) => { const [level, setLevel] = useState(""); const dispatch = useDispatch(); const [openLangModal, setOpenLangModal] = useState(false); - const [lang, setLang] = useState(getLocalData("lang") || "en"); + const [lang, setLang] = useState(getLanguageOrDefault()); const [points, setPoints] = useState(0); const [vocabCount, setVocabCount] = useState(0); const [wordCount, setWordCount] = useState(0); + const [milestoneDataKey, setMilestoneDataKey] = useState(0); // Force re-render when milestone data changes const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const [showModal, setShowModal] = useState(false); const nativeLangEnable = getLocalData("nativeLangEnable"); @@ -959,6 +1456,38 @@ const Assesment = ({ discoverStart }) => { const rStepNo = getLocalData("rStepZero"); const rFlows = String(getLocalData("rFlow")); + const getAssessmentText = () => { + const texts = { + en: { + testSkills: "Let's test your language skills", + goodSkills: "You have good language skills", + discoverLevel: "Take the assessment to discover your level", + completeLevel: (level) => + `Take the assessment to complete Level ${level}.`, + startAssessment: "Start Assessment", + }, + te: { + testSkills: "మీ భాషా నైపుణ్యాలను పరీక్షించుకుందాం", + goodSkills: "మీకు మంచి భాషా నైపుణ్యాలు ఉన్నాయి", + discoverLevel: "మీ స్థాయిని తెలుసుకోవడానికి మూల్యాంకనాన్ని చేయండి.", + completeLevel: (level) => + `Level ${level} పూర్తి చేయడానికి మూల్యాంకనాన్ని చేయండి.`, + startAssessment: "మూల్యాంకనాన్ని ప్రారంభించండి", + }, + kn: { + testSkills: "Let's test your language skills", + goodSkills: "You have good language skills", + discoverLevel: "Take the assessment to discover your level", + completeLevel: (level) => + `Take the assessment to complete Level ${level}.`, + startAssessment: "Start Assessment", + }, + }; + + return texts[lang] || texts.en; + }; + const assessmentText = getAssessmentText(); + const handleWordClick = () => { setShowModal(true); }; @@ -974,11 +1503,11 @@ const Assesment = ({ discoverStart }) => { (level === "B" && rFlows !== "true") ) { setLocalData("mFail", "true"); - setLocalData("rFlow", "true"); + // setLocalData("rFlow", "true"); setLocalData("rStepZero", 0); } - console.log("nLang", nativeLang, nativeLangEnable, level); + // console.log("nLang", nativeLang, nativeLangEnable, level); useEffect(() => { setLocalData("lang", lang); @@ -999,6 +1528,8 @@ const Assesment = ({ discoverStart }) => { "getMilestone", JSON.stringify({ ...getMilestoneDetails }) ); + // Force re-render to update milestone data display + setMilestoneDataKey((prev) => prev + 1); if ( levelMapping[usernameDetails?.data?.result?.virtualID] !== undefined @@ -1020,7 +1551,7 @@ const Assesment = ({ discoverStart }) => { } } - console.log("Assigned LEVEL:", level); + // console.log("Assigned LEVEL:", level); localStorage.setItem( "virtualId", @@ -1071,6 +1602,8 @@ const Assesment = ({ discoverStart }) => { "getMilestone", JSON.stringify({ ...getMilestoneDetails }) ); + // Force re-render to update milestone data display + setMilestoneDataKey((prev) => prev + 1); const level = getMilestoneDetails?.data?.milestone_level; setLevel( level?.startsWith("m") ? Number(level.replace("m", "")) : level @@ -1099,7 +1632,7 @@ const Assesment = ({ discoverStart }) => { } } - console.log("Assigned LEVEL:", level); + // console.log("Assigned LEVEL:", level); let sessionId = getLocalData("sessionId"); @@ -1178,6 +1711,20 @@ const Assesment = ({ discoverStart }) => { }); return; } + // When milestone_level is "B" and sub_milestone_level is empty (e.g. after discovery fail), route to letter-hunt + const milestoneDataForRedirect = getMilestoneData(); + const milestoneLevelForRedirect = + milestoneDataForRedirect?.data?.milestone_level || null; + const subMilestoneLevelForRedirect = + milestoneDataForRedirect?.data?.sub_milestone_level ?? ""; + if ( + (level === "B" || level === "b") && + milestoneLevelForRedirect === "B" && + (!subMilestoneLevelForRedirect || subMilestoneLevelForRedirect === "") + ) { + navigate("/letter-hunt"); + return; + } if (level === 0) { navigate("/discover"); } else { @@ -1219,10 +1766,64 @@ const Assesment = ({ discoverStart }) => { ? `desktopLevel${level || 1}Mobile` : `desktopLevel${level || 1}`; - const rFlow = String(getLocalData("rFlow")); const tFlow = String(getLocalData("tFlow")); const rStep = Number(getLocalData("rStep")) || 0; + // Get milestone_level from API to determine if F1 flow should be active + const getMilestoneData = () => { + try { + const milestoneStr = getLocalData("getMilestone"); + if (milestoneStr) { + return JSON.parse(milestoneStr); + } + } catch (e) { + console.error("Error parsing getMilestone:", e); + } + return null; + }; + const milestoneData = getMilestoneData(); + const milestoneLevel = milestoneData?.data?.milestone_level || null; + const subMilestoneLevel = milestoneData?.data?.sub_milestone_level || null; + + // Only set rFlow to "true" if milestone level is "B" (F1/F2/F3 flows) + // For milestone levels "m1", "m2", etc., rFlow should be "false" + const rFlowRaw = getLocalData("rFlow"); + const rFlow = + milestoneLevel === "B" && rFlowRaw === "true" ? "true" : "false"; + + // F1 flow is triggered when milestone_level is "B" and sub_milestone_level is "F1" + const shouldShowF1 = milestoneLevel === "B" && subMilestoneLevel === "F1"; + // F2 flow is triggered when milestone_level is "B" and sub_milestone_level is "F2" + const shouldShowF2 = milestoneLevel === "B" && subMilestoneLevel === "F2"; + // F3 flow is triggered when milestone_level is "B" and sub_milestone_level is "F3" + const shouldShowF3 = milestoneLevel === "B" && subMilestoneLevel === "F3"; + + // Check if F1 flow is active + const f1FlowStep = getF1FlowStep(); + const isF1FlowActive = shouldShowF1 && f1FlowStep.step !== null; + + // Check if F2 flow is active + const f2FlowStep = getF2FlowStep(); + const isF2FlowActive = shouldShowF2 && f2FlowStep.step !== null; + + // Check if F3 flow is active + const f3FlowStep = getF3FlowStep(); + const isF3FlowActive = shouldShowF3 && f3FlowStep.step !== null; + + // console.log("Discovery Start - F1/F2/F3 detection:", { + // milestoneLevel, + // subMilestoneLevel, + // shouldShowF1, + // shouldShowF2, + // shouldShowF3, + // isF1FlowActive, + // isF2FlowActive, + // isF3FlowActive, + // f1FlowStepIndex: f1FlowStep.index, + // f2FlowStepIndex: f2FlowStep.index, + // f3FlowStepIndex: f3FlowStep.index, + // }); + const sectionStyle = { width: "100vw", height: "100vh", @@ -1328,15 +1929,17 @@ const Assesment = ({ discoverStart }) => { textShadow: "#000 1px 0 10px", }} > - {rFlow === "true" - ? `Start Refresher ${ - [1, "B"]?.includes(level) - ? rStepNo == null || rStepNo === 0 || rStepNo === "0" - ? "0" - : "1" - : rStep - }` - : `Start Level ${level}`} + {shouldShowF3 + ? "Start F3" + : shouldShowF2 + ? "Start F2" + : isF1FlowActive + ? "Start F1" + : milestoneLevel === "B" && rFlow === "true" + ? `Learn Letters` + : // Only show "Learn Letters" for milestone level "B" (F1/F2/F3 flows) + // For milestone levels "m1", "m2", etc., show "Start Level X" + `Start Level ${level}`}
@@ -1375,15 +1978,15 @@ const Assesment = ({ discoverStart }) => { color: "#322020", fontWeight: 700, fontSize: { xs: "24px", md: "40px" }, - fontFamily: "Quicksand", + fontFamily: getFontFamily(lang), lineHeight: { xs: "36px", md: "62px" }, textAlign: "center", }} fontSize={{ md: "40px", xs: "30px" }} > {discoverStart - ? "Let's test your language skills" - : "You have good language skills"} + ? assessmentText.testSkills + : assessmentText.goodSkills} { color: "#1CB0F6", fontWeight: 600, fontSize: { xs: "20px", md: "30px" }, - fontFamily: "Quicksand", + fontFamily: getFontFamily(lang), lineHeight: { xs: "30px", md: "50px" }, textAlign: "center", }} fontSize={{ md: "30px", xs: "20px" }} > {level > 0 - ? `Take the assessment to complete Level ${level}.` - : "Take the assessment to discover your level"} + ? assessmentText.completeLevel(level) + : assessmentText.discoverLevel} @@ -1424,7 +2027,35 @@ const Assesment = ({ discoverStart }) => { }} onClick={handleRedirect} > - + {lang === "te" ? ( + + + {assessmentText.startAssessment} + + + ) : ( + + )} diff --git a/src/components/AssesmentEnd/AssesmentEnd.jsx b/src/components/AssesmentEnd/AssesmentEnd.jsx index 2cc24e5b..38d95fdc 100644 --- a/src/components/AssesmentEnd/AssesmentEnd.jsx +++ b/src/components/AssesmentEnd/AssesmentEnd.jsx @@ -12,7 +12,7 @@ import homeBackground from "../../assets/images/homeBackground.png"; import { Typography } from "../../../node_modules/@mui/material/index"; import { useNavigate } from "react-router-dom"; import axios from "axios"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import LevelCompleteAudio from "../../assets/audio/levelComplete.wav"; import { ProfileHeader } from "../Assesment/Assesment"; import desktopLevel5 from "../../assets/images/assesmentComplete.png"; @@ -31,13 +31,20 @@ const AssesmentEnd = () => { const [vocabCount, setVocabCount] = useState(0); const [wordCount, setWordCount] = useState(0); const lang = getLocalData("lang"); + const hasFetchedData = useRef(false); useEffect(() => { + if (levelCompleteAudioSrc && !hasFetchedData.current) { + let audio = new Audio(levelCompleteAudioSrc); + audio.play(); + } + }, [levelCompleteAudioSrc]); + + useEffect(() => { + if (hasFetchedData.current) return; + (async () => { - if (levelCompleteAudioSrc) { - let audio = new Audio(levelCompleteAudioSrc); - audio.play(); - } + hasFetchedData.current = true; const virtualId = getLocalData("virtualId"); const lang = getLocalData("lang"); const previous_level = getLocalData("previous_level"); @@ -70,7 +77,7 @@ const AssesmentEnd = () => { setTimeout(() => { setShake(false); }, 4000); - }, [levelCompleteAudioSrc]); + }, []); const navigate = useNavigate(); let newLevel = level.replace("m", ""); diff --git a/src/components/AudioDiagnostic/AudioDiagnosticModal.css b/src/components/AudioDiagnostic/AudioDiagnosticModal.css new file mode 100644 index 00000000..7337f23f --- /dev/null +++ b/src/components/AudioDiagnostic/AudioDiagnosticModal.css @@ -0,0 +1,29 @@ +@keyframes pulse { + 0%, 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.7; + transform: scale(1.1); + } +} + +@keyframes soundWave { + 0%, 100% { + transform: scaleY(0.3); + opacity: 0.5; + } + 50% { + transform: scaleY(1); + opacity: 1; + } +} + +/* Responsive adjustments */ +@media (max-width: 600px) { + .MuiCard-root { + margin: 10px; + } +} + diff --git a/src/components/AudioDiagnostic/AudioDiagnosticModal.jsx b/src/components/AudioDiagnostic/AudioDiagnosticModal.jsx new file mode 100644 index 00000000..d858270c --- /dev/null +++ b/src/components/AudioDiagnostic/AudioDiagnosticModal.jsx @@ -0,0 +1,2281 @@ +import React, { useState, useRef, useEffect } from "react"; +import { + Box, + Button, + Typography, + Card, + CardContent, + CircularProgress, + Alert, + Grid, + LinearProgress, + Fade, +} from "@mui/material"; +import { useMediaQuery, useTheme } from "@mui/material"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import ErrorIcon from "@mui/icons-material/Error"; +import MicIcon from "@mui/icons-material/Mic"; +import VolumeUpIcon from "@mui/icons-material/VolumeUp"; +import PlayArrowIcon from "@mui/icons-material/PlayArrow"; +import RefreshIcon from "@mui/icons-material/Refresh"; +import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; +import { getLocalData } from "../../utils/constants"; +import textureImage from "../../assets/images/textureImage.png"; +import panda from "../../assets/images/panda.svg"; +import { impression, interact, Log } from "../../services/telementryService"; +import { getRandomAudioPrompt } from "../../constants/audioDiagnosticPrompts"; +import { getTranslations } from "../../constants/audioDiagnosticTranslations"; +import SpeechRecognition, { + useSpeechRecognition, +} from "react-speech-recognition"; +import "./AudioDiagnosticModal.css"; + +const AudioDiagnosticModal = ({ show, onClose }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [micStatus, setMicStatus] = useState("pending"); + const [speakerStatus, setSpeakerStatus] = useState("pending"); + const [micError, setMicError] = useState(""); + const [speakerError, setSpeakerError] = useState(""); + const [isRecording, setIsRecording] = useState(false); + const [isPlaying, setIsPlaying] = useState(false); + const [isPlayingBack, setIsPlayingBack] = useState(false); + const [recordingProgress, setRecordingProgress] = useState(0); + const [recordingTimeRemaining, setRecordingTimeRemaining] = useState(5); + const [audioLevel, setAudioLevel] = useState(0); + const [recordedAudioUrl, setRecordedAudioUrl] = useState(null); + const [testMessage, setTestMessage] = useState(""); + const [lang, setLang] = useState(getLocalData("lang") || "en"); + const [audioPrompt, setAudioPrompt] = useState(""); + const [currentStep, setCurrentStep] = useState("mic"); // "mic" or "speaker" + const [isPlayingPrompt, setIsPlayingPrompt] = useState(false); + const [hasListenedToPrompt, setHasListenedToPrompt] = useState(false); + + const mediaStreamRef = useRef(null); + const mediaRecorderRef = useRef(null); + const audioContextRef = useRef(null); + const analyserRef = useRef(null); + const testAudioRef = useRef(null); + const playbackAudioRef = useRef(null); + const recordedChunksRef = useRef([]); + const speakerTestPassedRef = useRef(false); + const animationFrameRef = useRef(null); + const recordingTimerRef = useRef(null); + const audioDetectedRef = useRef(false); + const audioLevelsRef = useRef([]); + const micTestStartTimeRef = useRef(null); + const speakerTestStartTimeRef = useRef(null); + const transcriptRef = useRef(""); + + // Get browser speech recognition transcript + const { transcript, resetTranscript, browserSupportsSpeechRecognition } = + useSpeechRecognition(); + + // Update transcript ref when transcript changes + useEffect(() => { + transcriptRef.current = transcript; + }, [transcript]); + + // Get translations based on current language + const translations = getTranslations(lang); + + // Map language codes to browser speech recognition format + const getBrowserLanguage = (langCode) => { + const browserLangMap = { + en: "en-US", + hi: "hi-IN", + te: "te-IN", + ka: "kn-IN", + kn: "kn-IN", + ta: "ta-IN", + }; + return browserLangMap[langCode] || "en-US"; + }; + + // Reset all state to initial values + const resetDiagnostic = () => { + // First, clean up any ongoing operations (before resetting state) + cleanup(); + + // Cancel any ongoing speech synthesis + if ("speechSynthesis" in window) { + window.speechSynthesis.cancel(); + } + + // Reset all state variables + setMicStatus("pending"); + setSpeakerStatus("pending"); + setMicError(""); + setSpeakerError(""); + setIsRecording(false); + setIsPlaying(false); + setIsPlayingBack(false); + setRecordingProgress(0); + setRecordingTimeRemaining(5); + setAudioLevel(0); + setRecordedAudioUrl(null); + setTestMessage(""); + setCurrentStep("mic"); + setIsPlayingPrompt(false); + setHasListenedToPrompt(false); + + // Reset all refs + speakerTestPassedRef.current = false; + audioDetectedRef.current = false; + audioLevelsRef.current = []; + micTestStartTimeRef.current = null; + speakerTestStartTimeRef.current = null; + recordedChunksRef.current = []; + transcriptRef.current = ""; + + // Reset speech recognition transcript if supported + if (browserSupportsSpeechRecognition) { + try { + resetTranscript(); + } catch (e) { + // Ignore errors + } + } + }; + + useEffect(() => { + if (show) { + // Reset everything to start fresh + resetDiagnostic(); + + // Impression event when diagnostic modal is displayed + impression("audio-diagnostics", "ET"); + + // Refresh language from localStorage when modal opens + const currentLang = getLocalData("lang") || "en"; + setLang(currentLang); + + // Generate audio prompt with current language + const prompt = getRandomAudioPrompt(currentLang); + setAudioPrompt(prompt); + + // Auto-play the audio prompt when modal opens + setTimeout(() => { + playPromptAudio(); + }, 500); + } + + return () => { + cleanup(); + // Cancel any ongoing speech synthesis + if ("speechSynthesis" in window) { + window.speechSynthesis.cancel(); + } + }; + }, [show]); + + // Load speech synthesis voices + const [voicesLoaded, setVoicesLoaded] = useState(false); + + useEffect(() => { + if ("speechSynthesis" in window) { + // Load voices (some browsers need this, especially Brave) + const loadVoices = () => { + const voices = window.speechSynthesis.getVoices(); + if (voices.length > 0) { + setVoicesLoaded(true); + } + }; + + // Try to load voices immediately + loadVoices(); + + // Some browsers (like Brave) need the voiceschanged event + if (window.speechSynthesis.onvoiceschanged !== undefined) { + window.speechSynthesis.onvoiceschanged = loadVoices; + } + + // Fallback: try loading voices after a short delay (for browsers that load them asynchronously) + const timeout = setTimeout(() => { + loadVoices(); + }, 100); + + return () => clearTimeout(timeout); + } + }, []); + + useEffect(() => { + if (isRecording && analyserRef.current) { + const updateAudioLevel = () => { + if (analyserRef.current) { + // Use getByteTimeDomainData for more accurate audio level detection + const dataArray = new Uint8Array( + analyserRef.current.frequencyBinCount + ); + analyserRef.current.getByteTimeDomainData(dataArray); + + // Calculate RMS (Root Mean Square) for audio level + let sum = 0; + for (let i = 0; i < dataArray.length; i++) { + const normalized = (dataArray[i] - 128) / 128; + sum += normalized * normalized; + } + const rms = Math.sqrt(sum / dataArray.length); + + // Store audio level for analysis + audioLevelsRef.current.push(rms); + + // Keep only last 50 samples (about 1 second at 50fps) + if (audioLevelsRef.current.length > 50) { + audioLevelsRef.current.shift(); + } + + // Check if audio is detected - very lenient to allow all speech + // Require actual sound, not just background noise or muted mic + const SILENCE_THRESHOLD = 0.003; // Very low threshold - allows very quiet speech + if (rms > SILENCE_THRESHOLD) { + audioDetectedRef.current = true; + } + + // Also mark as detected if we see any variation (indicates speech activity) + // Speech has more variation than silence or muted mic + if (audioLevelsRef.current.length > 5) { + const recentLevels = audioLevelsRef.current.slice(-5); + const maxLevel = Math.max(...recentLevels); + const minLevel = Math.min(...recentLevels); + const variation = maxLevel - minLevel; + // Very lenient - any variation with some peak indicates speech + if (variation > 0.005 && maxLevel > 0.005) { + // Any variation with peak indicates speech, not muted mic + audioDetectedRef.current = true; + } + } + + // For visualization, use a normalized value + setAudioLevel(Math.min(rms * 10, 1)); + } + if (isRecording) { + animationFrameRef.current = requestAnimationFrame(updateAudioLevel); + } + }; + updateAudioLevel(); + } else { + setAudioLevel(0); + if (animationFrameRef.current) { + cancelAnimationFrame(animationFrameRef.current); + } + } + return () => { + if (animationFrameRef.current) { + cancelAnimationFrame(animationFrameRef.current); + } + }; + }, [isRecording]); + + const cleanup = () => { + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + try { + mediaRecorderRef.current.stop(); + } catch (e) { + // Ignore errors when stopping + } + } + if (mediaStreamRef.current) { + mediaStreamRef.current.getTracks().forEach((track) => track.stop()); + mediaStreamRef.current = null; + } + if (testAudioRef.current) { + testAudioRef.current.pause(); + testAudioRef.current = null; + } + if (playbackAudioRef.current) { + playbackAudioRef.current.pause(); + playbackAudioRef.current = null; + } + // Clean up recorded audio URL if it exists + setRecordedAudioUrl((prevUrl) => { + if (prevUrl) { + URL.revokeObjectURL(prevUrl); + } + return null; + }); + if (audioContextRef.current) { + try { + audioContextRef.current.close(); + } catch (e) { + // Ignore errors when closing + } + audioContextRef.current = null; + } + if (recordingTimerRef.current) { + clearInterval(recordingTimerRef.current); + recordingTimerRef.current = null; + } + if (animationFrameRef.current) { + cancelAnimationFrame(animationFrameRef.current); + animationFrameRef.current = null; + } + }; + + const playPromptAudio = () => { + if (!audioPrompt) return; + + // Check for speech synthesis support across all browsers + if (!("speechSynthesis" in window)) { + console.warn("Speech synthesis not supported in this browser"); + // Still allow them to proceed + setIsPlayingPrompt(false); + setHasListenedToPrompt(true); + return; + } + + // Cancel any ongoing speech + window.speechSynthesis.cancel(); + + setIsPlayingPrompt(true); + + const utterance = new SpeechSynthesisUtterance(audioPrompt); + // Use the lang state variable + const currentLang = lang || getLocalData("lang") || "en"; + + // Map language codes to speech synthesis language codes + const langMap = { + hi: "hi-IN", + ta: "ta-IN", + tn: "ta-IN", // Tamil alternative code + te: "te-IN", + ka: "kn-IN", + kn: "kn-IN", + en: "en-US", + }; + + // Set language for speech synthesis + const targetLang = langMap[currentLang] || "en-US"; + utterance.lang = targetLang; + utterance.rate = 0.8; // Slightly slower for children + utterance.pitch = 1; + utterance.volume = 1; + + // Helper function to set voice and speak + const setVoiceAndSpeak = (voices) => { + // Try to find appropriate voice + let preferredVoice = voices.find( + (v) => + v.lang === targetLang || v.lang.startsWith(targetLang.split("-")[0]) + ); + + // If no preferred voice found, try to find any English voice as fallback + if (!preferredVoice) { + preferredVoice = voices.find((v) => v.lang.startsWith("en")); + } + + // If still no voice, use first available voice + if (!preferredVoice && voices.length > 0) { + preferredVoice = voices[0]; + } + + // Only set voice if available (some browsers like Safari on iOS need this) + if (preferredVoice) { + try { + utterance.voice = preferredVoice; + } catch (error) { + // Some browsers don't allow setting voice, use default + console.warn("Could not set voice, using default:", error); + } + } + + // Set up event handlers with timeout fallback + let timeoutId = null; + + utterance.onstart = () => { + // Clear timeout if speech starts + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + + utterance.onend = () => { + if (timeoutId) clearTimeout(timeoutId); + setIsPlayingPrompt(false); + setHasListenedToPrompt(true); + }; + + utterance.onerror = (error) => { + if (timeoutId) clearTimeout(timeoutId); + console.error("Speech synthesis error:", error); + setIsPlayingPrompt(false); + // Still allow them to proceed even if audio fails + setHasListenedToPrompt(true); + }; + + // Fallback timeout for browsers that don't fire events properly + timeoutId = setTimeout(() => { + setIsPlayingPrompt(false); + setHasListenedToPrompt(true); + }, 5000); // 5 second timeout + + // Try to speak with error handling + try { + window.speechSynthesis.speak(utterance); + } catch (error) { + if (timeoutId) clearTimeout(timeoutId); + console.error("Error speaking:", error); + setIsPlayingPrompt(false); + setHasListenedToPrompt(true); + } + }; + + // Get voices - handle different browser behaviors + const attemptSpeak = (retryCount = 0) => { + let voices = window.speechSynthesis.getVoices(); + + // If no voices and we haven't retried too many times + if (voices.length === 0 && retryCount < 3) { + // Wait and try again (for browsers like Brave, Safari that load voices asynchronously) + setTimeout(() => { + attemptSpeak(retryCount + 1); + }, 200 * (retryCount + 1)); // Exponential backoff + } else { + // Either we have voices or we've retried enough + if (voices.length > 0) { + setVoiceAndSpeak(voices); + } else { + // No voices available, try with default + console.warn( + "No voices loaded, attempting to speak with default voice" + ); + setVoiceAndSpeak([]); + } + } + }; + + // Start attempting to speak + attemptSpeak(); + }; + + const testMicrophone = async () => { + setMicStatus("testing"); + setMicError(""); + setRecordingProgress(0); + recordedChunksRef.current = []; + audioDetectedRef.current = false; + audioLevelsRef.current = []; + micTestStartTimeRef.current = Date.now(); + + // Reset transcript + if (browserSupportsSpeechRecognition) { + resetTranscript(); + transcriptRef.current = ""; + } + + // Don't regenerate prompt - use the one already set + if (!audioPrompt) { + const currentLang = getLocalData("lang") || "en"; + const prompt = getRandomAudioPrompt(currentLang); + setAudioPrompt(prompt); + } + + // Interact event for button click + interact("ET", "Test Microphone", "audio-diagnostics"); + + // Start browser speech recognition to capture transcript + if (browserSupportsSpeechRecognition) { + try { + const browserLang = getBrowserLanguage(lang); + SpeechRecognition.startListening({ + continuous: true, + interimResults: true, + language: browserLang, + }); + } catch (srError) { + // Speech recognition not available - continue with audio level detection only + } + } + + try { + // Check for getUserMedia support with fallbacks for all browsers + let stream; + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + // Fallback for older browsers + const getUserMedia = + navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + navigator.msGetUserMedia; + + if (!getUserMedia) { + throw new Error("Microphone access is not supported in this browser"); + } + + // Use legacy API with Promise wrapper + stream = await new Promise((resolve, reject) => { + getUserMedia.call(navigator, { audio: true }, resolve, reject); + }); + } else { + // Use modern API with audio constraints for better quality + stream = await navigator.mediaDevices.getUserMedia({ + audio: { + echoCancellation: true, + noiseSuppression: true, + autoGainControl: true, + }, + }); + } + mediaStreamRef.current = stream; + + // AudioContext with fallback for Safari/WebKit + const audioContext = new (window.AudioContext || + window.webkitAudioContext || + window.mozAudioContext)(); + analyserRef.current = audioContext.createAnalyser(); + analyserRef.current.fftSize = 2048; + analyserRef.current.smoothingTimeConstant = 0.8; + const source = audioContext.createMediaStreamSource(stream); + source.connect(analyserRef.current); + + // Check for MediaRecorder support with fallback mime types for all browsers + let mimeType = "audio/webm"; + const supportedTypes = [ + "audio/webm", + "audio/webm;codecs=opus", + "audio/ogg;codecs=opus", + "audio/mp4", + "audio/mpeg", + ]; + + // Find first supported mime type + for (const type of supportedTypes) { + if (MediaRecorder.isTypeSupported(type)) { + mimeType = type; + break; + } + } + + // If no supported type found, use default (browser will choose) + if (!MediaRecorder.isTypeSupported(mimeType)) { + console.warn("No preferred mime type supported, using browser default"); + mimeType = ""; // Let browser choose + } + + const mediaRecorder = new MediaRecorder( + stream, + mimeType + ? { + mimeType: mimeType, + } + : {} + ); + + mediaRecorderRef.current = mediaRecorder; + + mediaRecorder.ondataavailable = (event) => { + if (event.data.size > 0) { + recordedChunksRef.current.push(event.data); + } + }; + + mediaRecorder.onstop = async () => { + // Stop speech recognition and get final transcript + let finalTranscript = ""; + if (browserSupportsSpeechRecognition) { + try { + SpeechRecognition.stopListening(); + // Wait a bit for final transcript to be processed + await new Promise((resolve) => setTimeout(resolve, 500)); + finalTranscript = transcriptRef.current || transcript || ""; + } catch (e) { + finalTranscript = transcriptRef.current || transcript || ""; + } + } + + const hasTranscript = finalTranscript.trim().length > 0; + + // Analyze the recorded audio to check if actual sound was detected + const hasAudioData = recordedChunksRef.current.length > 0; + // Determine blob type based on what was recorded + const blobType = mediaRecorderRef.current?.mimeType || "audio/webm"; + const blob = hasAudioData + ? new Blob(recordedChunksRef.current, { type: blobType }) + : null; + + // Check multiple conditions: + // 1. Audio data was recorded + // 2. Audio was actually detected (not just silence) + // 3. Average audio level was above threshold OR we have significant blob size + const averageLevel = + audioLevelsRef.current.length > 0 + ? audioLevelsRef.current.reduce((a, b) => a + b, 0) / + audioLevelsRef.current.length + : 0; + + // Lenient thresholds to allow all speech while trying to catch muted microphones + // Muted mic typically shows very low levels (< 0.002) with minimal variation + const SILENCE_THRESHOLD = 0.003; // Very low - allows very quiet speech + const MIN_PEAK_LEVEL = 0.005; // Very low peak requirement (allows quiet speech) + const MIN_AVERAGE_LEVEL = 0.002; // Very low average requirement (allows quiet speech) + + // Get max level to check for any significant audio activity + const maxLevel = + audioLevelsRef.current.length > 0 + ? Math.max(...audioLevelsRef.current) + : 0; + + // Get min level - muted mic will have very low minimum + const minLevel = + audioLevelsRef.current.length > 0 + ? Math.min(...audioLevelsRef.current) + : 0; + + // Calculate how many samples are above threshold (sustained audio, not just noise) + const samplesAboveThreshold = + audioLevelsRef.current.length > 0 + ? audioLevelsRef.current.filter( + (level) => level > SILENCE_THRESHOLD + ).length + : 0; + const sustainedAudioRatio = + audioLevelsRef.current.length > 0 + ? samplesAboveThreshold / audioLevelsRef.current.length + : 0; + + // Calculate variation - speech has high variation, muted mic has low variation + const audioVariation = maxLevel - minLevel; + + // Check if we have actual audio - very lenient detection: + // Require at least ONE of the following to allow all speech: + // 1. Audio was detected (audioDetectedRef.current is true) OR + // 2. Either average level OR peak level is above minimum OR + // 3. At least 15% of samples show sustained audio OR + // 4. Variation is present (speech pattern, not flat line of muted mic) + // This is very lenient to allow quiet speech, but we'll add a muted mic check separately + const hasDetectedAudio = audioDetectedRef.current; + const hasAudioLevels = + averageLevel > MIN_AVERAGE_LEVEL || maxLevel > MIN_PEAK_LEVEL; + const hasSustainedAudio = sustainedAudioRatio > 0.15; // At least 15% of samples + const hasVariation = audioVariation > 0.003; // Some variation + + // Check for muted mic specifically - very low levels with no variation + const isLikelyMutedMic = + maxLevel < 0.002 && + averageLevel < 0.001 && + audioVariation < 0.001 && + sustainedAudioRatio < 0.1; + + // Pass if we have any indication of audio AND it's not a muted mic + const hasActualAudio = + !isLikelyMutedMic && + (hasDetectedAudio || + hasAudioLevels || + hasSustainedAudio || + hasVariation); + + setIsRecording(false); + setRecordingProgress(0); + + const testDuration = micTestStartTimeRef.current + ? ((Date.now() - micTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Additional check: muted mic often produces very small blobs + // But we'll be lenient here - only use blob size as a secondary check + // Normal speech recording should produce blobs > 1000 bytes for 3 seconds + const MIN_BLOB_SIZE = 800; // Very low minimum - allows quiet speech + const hasReasonableBlobSize = blob && blob.size > MIN_BLOB_SIZE; + + // If blob size is very small AND we have muted mic indicators, fail + // Otherwise, be lenient with blob size + const blobSizeCheck = hasReasonableBlobSize || !isLikelyMutedMic; + + // Only pass if we have audio data AND actual audio was detected + // If speech recognition is available, REQUIRE transcript (user must have spoken words) + // If speech recognition is not available, fall back to audio level detection + const transcriptRequired = browserSupportsSpeechRecognition; + const transcriptCheck = transcriptRequired ? hasTranscript : true; + + const testPassed = + hasAudioData && + blob && + blob.size > 0 && + hasActualAudio && + blobSizeCheck && + transcriptCheck; + + if (testPassed) { + // Create audio URL for playback + const audioUrl = URL.createObjectURL(blob); + setRecordedAudioUrl(audioUrl); + + // Set fun message with clear explanation + setTestMessage(""); + + // Log test result - recording successful + Log( + `Microphone test - Recording successful. Duration: ${testDuration}s, Audio detected: true, Average level: ${averageLevel.toFixed( + 4 + )}, Max level: ${maxLevel.toFixed(4)}, Blob size: ${ + blob.size + } bytes`, + "audio-diagnostics", + "ET" + ); + + // Mark microphone test as passed immediately (no playback - will play in speaker test) + setTimeout(() => { + setMicStatus("passed"); + setTestMessage(""); + + // Log test result - passed + Log( + `Microphone test - PASSED. Duration: ${testDuration}s`, + "audio-diagnostics", + "ET" + ); + + // Give children time before moving to next step + setTimeout(() => { + // Move to speaker test after mic test passes + if (speakerStatus === "pending") { + setCurrentStep("speaker"); + setTestMessage(""); + } + }, 1500); + }, 1500); + } else if (!hasAudioData || !blob || blob.size === 0) { + setMicStatus("failed"); + setMicError(getTranslations(lang).micErrorNoAudio); + // Log test result - failed + Log( + `Microphone test - FAILED. Duration: ${testDuration}s, Reason: No audio data recorded`, + "audio-diagnostics", + "ET" + ); + audioContext.close(); + } else if (browserSupportsSpeechRecognition && !hasTranscript) { + // We have audio data but no transcript - user didn't speak words + setMicStatus("failed"); + setMicError(getTranslations(lang).micErrorMuted); + // Log test result - failed (no speech) + Log( + `Microphone test - FAILED. Duration: ${testDuration}s, Reason: No speech detected in transcript, Transcript: "${finalTranscript}", Blob size: ${ + blob ? blob.size : 0 + } bytes`, + "audio-diagnostics", + "ET" + ); + audioContext.close(); + } else { + // We have blob data but no actual audio was detected (muted or silent) + setMicStatus("failed"); + setMicError(getTranslations(lang).micErrorMuted); + // Log test result - failed (muted) + Log( + `Microphone test - FAILED. Duration: ${testDuration}s, Reason: Microphone muted or no sound detected, Average level: ${averageLevel.toFixed( + 4 + )}, Max level: ${maxLevel.toFixed(4)}, Blob size: ${ + blob ? blob.size : 0 + } bytes, Audio detected flag: ${audioDetectedRef.current}`, + "audio-diagnostics", + "ET" + ); + audioContext.close(); + } + }; + + mediaRecorder.onerror = (event) => { + setMicStatus("failed"); + setMicError(getTranslations(lang).micErrorGeneric); + setIsRecording(false); + setRecordingProgress(0); + audioContext.close(); + }; + + // Start recording + mediaRecorder.start(100); + + // Set recording state immediately so audio monitoring can start + // The analyser is already connected to the stream + setIsRecording(true); + + let progress = 0; + let timeRemaining = 5; + setRecordingTimeRemaining(5); + + recordingTimerRef.current = setInterval(() => { + progress += 20; // 20% per second for 5 seconds + setRecordingProgress(Math.min(progress, 100)); + timeRemaining = Math.max(0, 5 - Math.ceil(progress / 20)); + setRecordingTimeRemaining(timeRemaining); + }, 1000); + + setTimeout(() => { + if (mediaRecorder.state === "recording") { + mediaRecorder.stop(); + } + if (recordingTimerRef.current) { + clearInterval(recordingTimerRef.current); + } + setRecordingTimeRemaining(0); + }, 5000); + } catch (error) { + setMicStatus("failed"); + setMicError(getTranslations(lang).micErrorPermission); + setIsRecording(false); + setRecordingProgress(0); + } + }; + + const testSpeaker = async () => { + // Interact event for button click (must be called before async operations for mobile) + interact("ET", "Test Speaker", "audio-diagnostics"); + + // If we have recorded audio, use that instead of beep + if (recordedAudioUrl) { + setSpeakerStatus("testing"); + setSpeakerError(""); + setTestMessage(""); + setIsPlaying(true); + speakerTestPassedRef.current = false; + speakerTestStartTimeRef.current = Date.now(); + + // Play the recorded audio (their own voice) + // Must play immediately in response to user click for mobile browsers + const audio = new Audio(recordedAudioUrl); + testAudioRef.current = audio; + audio.volume = 0.8; + + // Preload audio for better mobile compatibility + audio.preload = "auto"; + + // Try to play immediately (mobile browsers require user gesture - no setTimeout!) + // This must be called synchronously in response to the button click + const playPromise = audio.play(); + if (playPromise !== undefined) { + playPromise.catch((err) => { + console.error("Audio play error:", err); + // If immediate play fails, try again when audio is loaded + audio.onloadeddata = () => { + audio.play().catch((playErr) => { + console.error("Audio play error (after load):", playErr); + if (!speakerTestPassedRef.current) { + setIsPlaying(false); + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerError); + + const testDuration = speakerTestStartTimeRef.current + ? ( + (Date.now() - speakerTestStartTimeRef.current) / + 1000 + ).toFixed(2) + : 0; + + Log( + `Speaker test - FAILED. Duration: ${testDuration}s, Error: ${ + playErr.message || playErr + }`, + "audio-diagnostics", + "ET" + ); + } + }); + }; + + if (!speakerTestPassedRef.current) { + setIsPlaying(false); + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerError); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed( + 2 + ) + : 0; + + Log( + `Speaker test - FAILED. Duration: ${testDuration}s, Error: ${ + err.message || err + }`, + "audio-diagnostics", + "ET" + ); + } + }); + } + + audio.onended = () => { + if (!speakerTestPassedRef.current) { + setIsPlaying(false); + setTestMessage(""); + + // Give time before marking as passed + setTimeout(() => { + speakerTestPassedRef.current = true; + setSpeakerStatus("passed"); + setTestMessage(""); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed( + 2 + ) + : 0; + + Log( + `Speaker test - PASSED. Duration: ${testDuration}s, Method: Recorded Audio Playback`, + "audio-diagnostics", + "ET" + ); + }, 1000); + } + if (testAudioRef.current) { + testAudioRef.current = null; + } + }; + + audio.onerror = (error) => { + console.error("Audio error event:", error); + if (!speakerTestPassedRef.current) { + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerError); + setIsPlaying(false); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + Log( + `Speaker test - FAILED. Duration: ${testDuration}s, Reason: Audio error event`, + "audio-diagnostics", + "ET" + ); + } + }; + + return; + } + + // Fallback to original beep test if no recorded audio + setSpeakerStatus("testing"); + setSpeakerError(""); + setIsPlaying(true); + speakerTestPassedRef.current = false; + speakerTestStartTimeRef.current = Date.now(); + + // Interact event for button click + interact("ET", "Test Speaker", "audio-diagnostics"); + + try { + audioContextRef.current = new (window.AudioContext || + window.webkitAudioContext)(); + + const oscillator = audioContextRef.current.createOscillator(); + const gainNode = audioContextRef.current.createGain(); + + oscillator.connect(gainNode); + gainNode.connect(audioContextRef.current.destination); + + oscillator.type = "sine"; + oscillator.frequency.value = 440; + gainNode.gain.setValueAtTime(0.3, audioContextRef.current.currentTime); + + oscillator.start(); + oscillator.stop(audioContextRef.current.currentTime + 1); + + oscillator.onended = () => { + if (!speakerTestPassedRef.current) { + speakerTestPassedRef.current = true; + setSpeakerStatus("passed"); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - passed + Log( + `Speaker test - PASSED. Duration: ${testDuration}s, Method: Web Audio API`, + "audio-diagnostics", + "ET" + ); + } + setIsPlaying(false); + if (audioContextRef.current) { + audioContextRef.current.close(); + audioContextRef.current = null; + } + }; + + oscillator.onerror = () => { + if (!speakerTestPassedRef.current) { + tryHTML5AudioTest(); + } + }; + + setTimeout(() => { + if (!speakerTestPassedRef.current) { + tryHTML5AudioTest(); + } + }, 1200); + } catch (error) { + tryHTML5AudioTest(); + } + }; + + const tryHTML5AudioTest = () => { + try { + const testAudio = new Audio(); + testAudioRef.current = testAudio; + + const audioData = generateBeepSound(); + testAudio.src = audioData; + testAudio.volume = 0.5; + testAudio.preload = "auto"; + + // Try to play immediately (mobile browsers require user gesture) + // This is called from testSpeaker which is triggered by user click + const playPromise = testAudio.play(); + if (playPromise !== undefined) { + playPromise.catch((err) => { + console.error("Audio play error (immediate):", err); + // If immediate play fails, wait for canplaythrough as fallback + testAudio.oncanplaythrough = () => { + testAudio.play().catch((playErr) => { + console.error("Audio play error (after load):", playErr); + if (!speakerTestPassedRef.current) { + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerError); + setIsPlaying(false); + } + }); + }; + }); + } else { + // Fallback: wait for audio to be ready + testAudio.oncanplaythrough = () => { + testAudio.play().catch((err) => { + console.error("Audio play error:", err); + if (!speakerTestPassedRef.current) { + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerError); + setIsPlaying(false); + } + }); + }; + } + + testAudio.onended = () => { + if (!speakerTestPassedRef.current) { + speakerTestPassedRef.current = true; + setSpeakerStatus("passed"); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - passed (HTML5 fallback) + Log( + `Speaker test - PASSED. Duration: ${testDuration}s, Method: HTML5 Audio`, + "audio-diagnostics", + "ET" + ); + } + setIsPlaying(false); + }; + + testAudio.onerror = () => { + if (!speakerTestPassedRef.current) { + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerErrorGeneric); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - failed + Log( + `Speaker test - FAILED. Duration: ${testDuration}s, Reason: Audio playback error`, + "audio-diagnostics", + "ET" + ); + + setIsPlaying(false); + } + }; + } catch (error) { + if (!speakerTestPassedRef.current) { + setSpeakerStatus("failed"); + setSpeakerError(getTranslations(lang).speakerErrorNoTest); + + const testDuration = speakerTestStartTimeRef.current + ? ((Date.now() - speakerTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - failed + Log( + `Speaker test - FAILED. Duration: ${testDuration}s, Reason: ${ + error.message || "Unknown error" + }`, + "audio-diagnostics", + "ET" + ); + + setIsPlaying(false); + } + } + }; + + const playRecordedAudio = (audioUrl) => { + if (!audioUrl) return; + + setIsPlayingBack(true); + setTestMessage(""); + + // Clean up any existing playback + if (playbackAudioRef.current) { + playbackAudioRef.current.pause(); + playbackAudioRef.current = null; + } + + const audio = new Audio(audioUrl); + playbackAudioRef.current = audio; + + audio.onended = () => { + setIsPlayingBack(false); + setTestMessage(""); + + const totalTestDuration = micTestStartTimeRef.current + ? ((Date.now() - micTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - passed (after playback) + Log( + `Microphone test - PASSED. Total duration: ${totalTestDuration}s, Playback: successful`, + "audio-diagnostics", + "ET" + ); + + // Mark microphone test as passed after successful playback + setTimeout(() => { + setMicStatus("passed"); + setTestMessage(""); + + // Give children time before moving to next step + setTimeout(() => { + // Move to speaker test after mic test passes + // Don't auto-play - let them click the button to avoid playing twice + if (speakerStatus === "pending") { + setCurrentStep("speaker"); + setTestMessage(""); + } + }, 1500); + }, 1500); + }; + + audio.onerror = () => { + setIsPlayingBack(false); + setMicStatus("failed"); + setMicError(getTranslations(lang).speakerError); + setTestMessage(""); + + const totalTestDuration = micTestStartTimeRef.current + ? ((Date.now() - micTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - failed (playback error) + Log( + `Microphone test - FAILED. Total duration: ${totalTestDuration}s, Reason: Playback failed`, + "audio-diagnostics", + "ET" + ); + }; + + audio.play().catch((err) => { + console.error("Playback error:", err); + setIsPlayingBack(false); + setMicStatus("failed"); + setMicError(getTranslations(lang).speakerError); + setTestMessage(""); + + const totalTestDuration = micTestStartTimeRef.current + ? ((Date.now() - micTestStartTimeRef.current) / 1000).toFixed(2) + : 0; + + // Log test result - failed (playback error) + Log( + `Microphone test - FAILED. Total duration: ${totalTestDuration}s, Reason: Playback error - ${ + err.message || "Unknown" + }`, + "audio-diagnostics", + "ET" + ); + }); + }; + + const generateBeepSound = () => { + const sampleRate = 44100; + const duration = 0.5; + const frequency = 440; + const samples = sampleRate * duration; + const buffer = new ArrayBuffer(44 + samples * 2); + const view = new DataView(buffer); + + const writeString = (offset, string) => { + for (let i = 0; i < string.length; i++) { + view.setUint8(offset + i, string.charCodeAt(i)); + } + }; + + writeString(0, "RIFF"); + view.setUint32(4, 36 + samples * 2, true); + writeString(8, "WAVE"); + writeString(12, "fmt "); + view.setUint32(16, 16, true); + view.setUint16(20, 1, true); + view.setUint16(22, 1, true); + view.setUint32(24, sampleRate, true); + view.setUint32(28, sampleRate * 2, true); + view.setUint16(32, 2, true); + view.setUint16(34, 16, true); + writeString(36, "data"); + view.setUint32(40, samples * 2, true); + + for (let i = 0; i < samples; i++) { + const sample = Math.sin((2 * Math.PI * frequency * i) / sampleRate); + view.setInt16(44 + i * 2, sample * 0x7fff, true); + } + + const blob = new Blob([buffer], { type: "audio/wav" }); + return URL.createObjectURL(blob); + }; + + const handleStartTests = () => { + // Interact event for button click + interact("ET", "Start All Tests", "audio-diagnostics"); + + setCurrentStep("mic"); + testMicrophone(); + }; + + const handleContinue = () => { + // Interact event for button click + interact("ET", "Continue to Application", "audio-diagnostics"); + + cleanup(); + onClose(); + }; + + const handleRetry = () => { + // Interact event for button click + interact("ET", "Retry Tests", "audio-diagnostics"); + + setMicStatus("pending"); + setSpeakerStatus("pending"); + setMicError(""); + setSpeakerError(""); + setRecordingProgress(0); + setRecordingTimeRemaining(5); + setAudioLevel(0); + setIsPlayingBack(false); + setTestMessage(""); + // Generate a new prompt for retry with current language + const currentLang = getLocalData("lang") || "en"; + setLang(currentLang); + const prompt = getRandomAudioPrompt(currentLang); + setAudioPrompt(prompt); + setHasListenedToPrompt(false); + setCurrentStep("mic"); + audioDetectedRef.current = false; + audioLevelsRef.current = []; + micTestStartTimeRef.current = null; + speakerTestStartTimeRef.current = null; + if (recordedAudioUrl) { + URL.revokeObjectURL(recordedAudioUrl); + } + setRecordedAudioUrl(null); + cleanup(); + }; + + const allTestsPassed = micStatus === "passed" && speakerStatus === "passed"; + const allTestsCompleted = + (micStatus === "passed" || micStatus === "failed") && + (speakerStatus === "passed" || speakerStatus === "failed"); + + const getStatusIcon = (status, type) => { + const iconSize = isMobile ? 50 : 70; + + // Show playback state for microphone + if (type === "mic" && isPlayingBack) { + return ( + + + + + + + ); + } + + switch (status) { + case "passed": + return ( + + ); + case "failed": + return ( + + ); + case "testing": + return ( + + + {(type === "mic" && isRecording) || + (type === "speaker" && isPlaying) ? ( + + {type === "mic" ? ( + + ) : ( + + )} + + ) : null} + + ); + default: + return type === "mic" ? ( + + ) : ( + + ); + } + }; + + if (!show) return null; + + return ( + + + + {/* Speech Bubble - Hide on error */} + {!(currentStep === "mic" && micStatus === "failed") && + !(currentStep === "speaker" && speakerStatus === "failed") && ( + + + + {currentStep === "mic" + ? micStatus === "pending" && !hasListenedToPrompt + ? translations.listenAndRepeat + : micStatus === "pending" && hasListenedToPrompt + ? translations.nowRepeat + : micStatus === "testing" || isRecording + ? translations.keepSpeaking + : micStatus === "passed" + ? translations.micTestPassed + : translations.testMicrophone + : speakerStatus === "pending" + ? translations.listenToVoice + : speakerStatus === "testing" || isPlaying + ? translations.canYouHear + : speakerStatus === "passed" + ? translations.speakerTestPassed + : translations.testSpeakers} + + + + )} + + {/* Centered Panda Mascot - Hide on error */} + {!(currentStep === "mic" && micStatus === "failed") && + !(currentStep === "speaker" && speakerStatus === "failed") && ( + + panda + + )} + + {/* Test Content - Simplified */} + + {currentStep === "mic" && ( + <> + {/* Reading Prompt - Show before and during recording, hide on error */} + {audioPrompt && (micStatus === "pending" || isRecording) && ( + + + {!isRecording && ( + + + {isPlayingPrompt + ? translations.playingAudio + : hasListenedToPrompt + ? translations.clickToListenAgain + : translations.clickToListen} + + + + )} + {isRecording && ( + <> + {/* Stopwatch-style countdown timer */} + {(() => { + // Determine color based on remaining time + // Green: 5-3 seconds, Orange: 2 seconds, Red: 1-0 seconds + let circleColor = "#6DAF19"; // Green + let textColor = "#6DAF19"; // Green + + if (recordingTimeRemaining <= 1) { + circleColor = "#f44336"; // Red + textColor = "#f44336"; // Red + } else if (recordingTimeRemaining <= 2) { + circleColor = "#ff9800"; // Orange + textColor = "#ff9800"; // Orange + } + + return ( + + {/* Circular progress background */} + + {/* Circular progress foreground with dynamic color */} + + {/* Timer number */} + + + {recordingTimeRemaining > 0 + ? recordingTimeRemaining + : "0"} + + + {recordingTimeRemaining > 0 + ? "seconds" + : "stopping"} + + + + ); + })()} + + + {[...Array(20)].map((_, i) => ( + + ))} + + + )} + + + )} + + {/* Status Icon - Only show when not recording and not in error state */} + {!isRecording && !isPlayingBack && micStatus !== "failed" && ( + + {getStatusIcon(micStatus, "mic")} + + )} + + )} + + {currentStep === "speaker" && ( + <> + {/* Status Icon */} + {!isPlaying && ( + + {getStatusIcon(speakerStatus, "speaker")} + + )} + + {/* Playing Section */} + {isPlaying && ( + + + + Can you hear it? + + + {[...Array(5)].map((_, i) => ( + + ))} + + + + )} + + )} + + + {/* Error Messages - Prominent on error screen */} + {micError && currentStep === "mic" && ( + + {/* Error Icon */} + + + + {/* Error Message */} + + {micError} + + + )} + + {speakerError && currentStep === "speaker" && ( + + {/* Error Icon */} + + + + {/* Error Message */} + + {speakerError} + + + )} + + {/* Action Buttons - Duolingo Style */} + + {currentStep === "mic" && + (micStatus === "pending" || micStatus === "failed") && + !isRecording && ( + + )} + + {currentStep === "speaker" && + (speakerStatus === "pending" || speakerStatus === "failed") && + !isPlaying && ( + + )} + + + + + {/* Skip Button - Always visible */} + + + + + {/* Bottom Action Buttons - Only show when all tests completed */} + {allTestsCompleted && ( + + + + + )} + + ); +}; + +export default AudioDiagnosticModal; diff --git a/src/components/AudioDiagnostic/index.js b/src/components/AudioDiagnostic/index.js new file mode 100644 index 00000000..553f4c04 --- /dev/null +++ b/src/components/AudioDiagnostic/index.js @@ -0,0 +1 @@ +export { default as AudioDiagnosticModal } from "./AudioDiagnosticModal"; diff --git a/src/components/CountdownTimer/CountdownTimer.jsx b/src/components/CountdownTimer/CountdownTimer.jsx new file mode 100644 index 00000000..0cb6f82b --- /dev/null +++ b/src/components/CountdownTimer/CountdownTimer.jsx @@ -0,0 +1,451 @@ +// import { useEffect, useState } from "react"; + +// const CountdownTimer = ({ initialCount = 3, onComplete }) => { +// const [count, setCount] = useState(initialCount); + +// useEffect(() => { +// if (count === 0) { +// onComplete(); +// return; +// } + +// const timer = setTimeout(() => { +// setCount(count - 1); +// }, 1000); + +// return () => clearTimeout(timer); +// }, [count, onComplete]); + +// return ( +//
+// {/* Countdown Number with magical animations */} +//
+//
+// {/* Multiple pulsing rings for magical effect */} +//
+//
+ +// {/* Main countdown circle with gradient */} +//
+// {/* Inner magical glow */} +//
+ +// {/* The countdown number */} +//
+// {count} +//
+//
+ +// {/* Floating sparkles */} +//
+// +//
+//
+// 💫 +//
+//
+// +//
+//
+// 🌟 +//
+ +// {/* Orbiting small stars */} +//
+//
+// ⭐ +//
+//
+// ✨ +//
+//
+// 💫 +//
+//
+// 🌟 +//
+//
+//
+//
+ +// {/* Progress dots - properly aligned */} +//
+// {[...Array(3)].map((_, index) => { +// const colors = [ +// "linear-gradient(135deg, #3B82F6 0%, #8B5CF6 100%)", +// "linear-gradient(135deg, #8B5CF6 0%, #EC4899 100%)", +// "linear-gradient(135deg, #EC4899 0%, #EF4444 100%)" +// ]; +// const isActive = count <= index + 1; +// return ( +//
+// ); +// })} +//
+ +// +//
+// ); +// }; + +// export default CountdownTimer; + +import { useEffect, useState } from "react"; + +const CountdownTimer = ({ initialCount = 3, onComplete }) => { + const [count, setCount] = useState(initialCount); + + useEffect(() => { + if (count === 0) { + onComplete(); + return; + } + + const timer = setTimeout(() => { + setCount(count - 1); + }, 1000); + + return () => clearTimeout(timer); + }, [count, onComplete]); + + return ( + //
+
+ {/* Countdown Number with magical animations */} +
+
+ {/* Multiple pulsing rings for magical effect */} +
+
+
+ + {/* Main countdown circle with gradient - Responsive sizing */} +
+ {/* Inner magical glow */} +
+ + {/* The countdown number - Responsive text */} +
+ {count} +
+
+ + {/* Floating sparkles with different animations - Responsive sizing */} +
+ +
+
+ 💫 +
+
+ +
+
+ 🌟 +
+ + {/* Orbiting small stars - Responsive sizing and positioning */} +
+
+ ⭐ +
+
+ ✨ +
+
+ 💫 +
+
+ 🌟 +
+
+
+
+ + {/* Progress dots with rainbow colors - Responsive sizing */} +
+ {[...Array(3)].map((_, index) => { + const colors = [ + "from-blue-500 to-purple-500", + "from-purple-500 to-pink-500", + "from-pink-500 to-red-500", + ]; + return ( +
+ ); + })} +
+
+ //
+ ); +}; + +export default CountdownTimer; diff --git a/src/components/CountdownTimer/index.js b/src/components/CountdownTimer/index.js new file mode 100644 index 00000000..bba6d281 --- /dev/null +++ b/src/components/CountdownTimer/index.js @@ -0,0 +1 @@ +export { default } from "./CountdownTimer"; diff --git a/src/components/DemoCompletionScreen/DemoCompletionScreen.jsx b/src/components/DemoCompletionScreen/DemoCompletionScreen.jsx new file mode 100644 index 00000000..2717c1ac --- /dev/null +++ b/src/components/DemoCompletionScreen/DemoCompletionScreen.jsx @@ -0,0 +1,332 @@ +import { Card, Button } from "@mui/material"; +import { ArrowLeft, Gamepad2, RotateCcw, Sparkles, Trophy } from "lucide-react"; + +const completionMessages = { + en: { + title: "Demo Complete!", + subtitle: "You're ready to start speaking!", + message: + "Great job! You've learned how to record and listen to your voice. Now it's time to practice with real sentences!", + startGame: "Start Speaking", + replayDemo: "Replay Demo", + back: "Back", + }, + te: { + title: "డెమో పూర్తయింది!", + subtitle: "మీరు మాట్లాడటం ప్రారంభించడానికి సిద్ధంగా ఉన్నారు!", + message: + "బాగా చేసారు! మీరు మీ గొంతును రికార్డ్ చేయడం మరియు వినడం ఎలా చేయాలో నేర్చుకున్నారు. ఇప్పుడు నిజమైన వాక్యాలతో అభ్యసించే సమయం!", + startGame: "మాట్లాడటం ప్రారంభించండి", + replayDemo: "డెమోను మళ్లీ ప్లే చేయండి", + back: "వెనుకకు", + }, + kn: { + title: "ಡೆಮೊ ಪೂರ್ಣಗೊಂಡಿದೆ!", + subtitle: "ನೀವು ಮಾತನಾಡಲು ಪ್ರಾರಂಭಿಸಲು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ!", + message: + "ಅದ್ಭುತ! ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುವುದು ಮತ್ತು ಕೇಳುವುದು ಹೇಗೆ ಎಂದು ನೀವು ಕಲಿತಿದ್ದೀರಿ. ಈಗ ನೈಜ ವಾಕ್ಯಗಳೊಂದಿಗೆ ಅಭ್ಯಾಸ ಮಾಡುವ ಸಮಯ!", + startGame: "ಮಾತನಾಡಲು ಪ್ರಾರಂಭಿಸಿ", + replayDemo: "ಡೆಮೊವನ್ನು ಮರು ಪ್ಲೇ ಮಾಡಿ", + back: "ಹಿಂದಕ್ಕೆ", + }, + mr: { + title: "डेमो पूर्ण झाला!", + subtitle: "तुम्ही बोलणे सुरू करण्यास तयार आहात!", + message: + "छान केलं! तुम्ही तुमचा आवाज रेकॉर्ड कसा करायचा आणि ऐकायचा हे शिकलात. आता खऱ्या वाक्यांसह सराव करण्याची वेळ आली आहे!", + startGame: "बोलणे सुरू करा", + replayDemo: "डेमो पुन्हा प्ले करा", + back: "मागे", + }, +}; + +const DemoCompletionScreen = ({ + language = "en", + onStartGame, + onReplayDemo, + onBack, + hideHeader = false, +}) => { + const messages = completionMessages[language] || completionMessages.en; + + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + + +
+ {/* Trophy Animation */} +
+
+ + + {/* Sparkles */} +
+ ✨ +
+
+ 🌟 +
+
+ ⭐ +
+
+
+ + {/* Title */} +
+ +

+ {messages.title} +

+ +
+ + {/* Subtitle */} +

+ {messages.subtitle} +

+ + {/* Message */} +
+

+ {messages.message} +

+
+ + {/* Buttons */} +
+ + + +
+
+
+
+ + +
+ ); +}; + +export default DemoCompletionScreen; diff --git a/src/components/DemoCompletionScreen/index.js b/src/components/DemoCompletionScreen/index.js new file mode 100644 index 00000000..d604d216 --- /dev/null +++ b/src/components/DemoCompletionScreen/index.js @@ -0,0 +1 @@ +export { default } from "./DemoCompletionScreen"; diff --git a/src/components/DiscoverSentance/DiscoverSentance.jsx b/src/components/DiscoverSentance/DiscoverSentance.jsx index b3fd5358..0d02a8a1 100644 --- a/src/components/DiscoverSentance/DiscoverSentance.jsx +++ b/src/components/DiscoverSentance/DiscoverSentance.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import { useNavigate } from "react-router-dom"; import axios from "../../../node_modules/axios/index"; import elephant from "../../assets/images/elephant.svg"; @@ -22,15 +22,21 @@ import { fetchUserPoints, createLearnerProgress, } from "../../services/orchestration/orchestrationService"; -import { fetchGetSetResult } from "../../services/learnerAi/learnerAiService"; +import { + fetchGetSetResult, + callEngagementPredictor, + clearInteractions, +} from "../../services/learnerAi/learnerAiService"; import { fetchAssessmentData, fetchPaginatedContent, } from "../../services/content/contentService"; +import DiscoverSentencePreview from "./DiscoverSentencePreview"; const SpeakSentenceComponent = () => { const [currentQuestion, setCurrentQuestion] = useState(0); const navigate = useNavigate(); + const [showDemo, setShowDemo] = useState(false); const [recordedAudio, setRecordedAudio] = useState(""); const [voiceText, setVoiceText] = useState(""); const [storyLine, setStoryLine] = useState(0); @@ -49,6 +55,8 @@ const SpeakSentenceComponent = () => { const [openMessageDialog, setOpenMessageDialog] = useState(""); const [totalSyllableCount, setTotalSyllableCount] = useState(""); const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); + const [interactions, setInteractions] = useState([]); + const interactionsRef = useRef([]); const levelCompleteAudioSrc = usePreloadAudio(LevelCompleteAudio); const sessionId = getLocalData("sessionId"); @@ -96,10 +104,34 @@ const SpeakSentenceComponent = () => { useEffect(() => { if (questions?.length) { - setLocalData("sub_session_id", uniqueId()); + const oldSubSessionId = getLocalData("sub_session_id"); + const newSubSessionId = uniqueId(); + setLocalData("sub_session_id", newSubSessionId); + + // Clear interactions for old sub session if it exists + if (oldSubSessionId) { + clearInteractions(oldSubSessionId); + } + + setInteractions([]); + interactionsRef.current = []; } }, [questions]); + useEffect(() => { + interactionsRef.current = interactions; + }, [interactions]); + + const handleInteractionComplete = (interactionData) => { + if (interactionData) { + setInteractions((prev) => { + const updated = [...prev, interactionData]; + interactionsRef.current = updated; + return updated; + }); + } + }; + useEffect(() => { if (voiceText === "error") { // alert(""); @@ -156,6 +188,11 @@ const SpeakSentenceComponent = () => { currentCollectionId, totalSyllableCount ); + + // Call engagement predictor after getsetresult + // Interactions are automatically retrieved from localStorage + callEngagementPredictor(sub_session_id); + if (!(localStorage.getItem("contentSessionId") !== null)) { let point = 1; let milestone = "m0"; @@ -231,17 +268,22 @@ const SpeakSentenceComponent = () => { setCurrentQuestion(0); setSentencePassedCounter(newSentencePassedCounter); setQuestions(quesArr); - } else if (getSetData.sessionResult === "pass" && lang === "en") { + setInteractions([]); + interactionsRef.current = []; + } else if ( + getSetData.sessionResult === "pass" && + currentContentType === "Sentence" + ) { //navigate("/discover-end"); - navigate("/towre-flow"); - } else if (getSetData.sessionResult === "pass" && lang !== "en") { - navigate("/discover-end"); + lang === "te" || lang == "en" + ? navigate("/towre-flow") + : navigate("/discover-end"); // all 3 passed mean sentence all are } else if ( getSetData.sessionResult === "fail" && currentContentType === "Sentence" ) { if (getSetData.currentLevel !== "m0") { - navigate("/letter-hunt"); + navigate("/discover-end"); } const words = assessmentResponse?.data?.find( (elem) => elem.category === "Word" @@ -256,26 +298,18 @@ const SpeakSentenceComponent = () => { let quesArr = [...(resWordsPagination?.data || [])]; setCurrentQuestion(0); setQuestions(quesArr); + setInteractions([]); + interactionsRef.current = []; } else if ( getSetData.sessionResult === "fail" && currentContentType === "Word" ) { - navigate("/letter-hunt"); + getSetData.currentLevel === "B" + ? navigate("/letter-hunt") + : navigate("/discover-end"); console.log("fail 2"); - - // const char = assessmentResponse?.data?.find( - // (elem) => elem.category === "Char" - // ); - // const resCharPagination = await axios.get( - // `${process.env.REACT_APP_LEARNER_AI_APP_HOST}/content-service/v1/content/pagination?page=1&limit=5&collectionId=${char?.content?.[0]?.collectionId}` - // ); - // setCurrentContentType("Char"); - // setCurrentCollectionId(char?.content?.[0]?.collectionId); - // setCurrentQuestion(0); - // let quesArr = [...(resCharPagination?.data?.data || [])]; - // setQuestions(quesArr); } else { - navigate("/letter-hunt"); + navigate("/discover-end"); console.log("fail 3"); } await addLesson({ @@ -349,8 +383,32 @@ const SpeakSentenceComponent = () => { useEffect(() => { localStorage.setItem("mechanism_id", ""); + + // Always show demo when entering discovery page + setShowDemo(true); }, []); + const handleDemoComplete = () => { + // Demo completed, now show the actual game + setShowDemo(false); + }; + + const handleDemoBack = () => { + const destination = + process.env.REACT_APP_IS_APP_IFRAME === "true" ? "/" : "/discover-start"; + navigate(destination); + }; + + // Show demo if first time + if (showDemo) { + return ( + + ); + } + return ( <> {!!openMessageDialog && ( @@ -394,6 +452,7 @@ const SpeakSentenceComponent = () => { isNextButtonCalled, setIsNextButtonCalled, setOpenMessageDialog, + onInteractionComplete: handleInteractionComplete, }} /> diff --git a/src/components/DiscoverSentance/DiscoverSentencePreview.jsx b/src/components/DiscoverSentance/DiscoverSentencePreview.jsx new file mode 100644 index 00000000..84a8609c --- /dev/null +++ b/src/components/DiscoverSentance/DiscoverSentencePreview.jsx @@ -0,0 +1,898 @@ +import { useState, useRef, useCallback } from "react"; +import { + getLocalData, + RetryIcon, + NextButtonRound, +} from "../../utils/constants"; +import CountdownTimer from "../CountdownTimer/CountdownTimer"; +import WordsOrImage from "../Mechanism/WordsOrImage"; +import { Sparkles } from "lucide-react"; +import { Progress } from "../../lib/axl-explorations/src/components/ui/progress"; +import { set } from "lodash"; + +const demoInstructions = { + en: { + demoSentence: "The cat is sleeping.", + narration1: + "See the sentence. The cat is sleeping. Now click the microphone button to record yourself saying it", + narration2: + "Now record the audio. Click the stop icon when you finish speaking", + narration3: "Click the play button to hear the recorded audio", + narration4: "Click on retry button to record the audio again", + narration5: "Click continue to proceed", + skipDemo: "Skip Demo", + startGame: "Start Game", + howToPlay: "How to Play", + }, + te: { + demoSentence: "పిల్లి నిద్రపోతోంది.", + narration1: + "వాక్యం చూడండి. పిల్లి నిద్రపోతోంది. ఇప్పుడు మైక్రోఫోన్ బటన్‌ను క్లిక్ చేసి మీరే చెప్పడానికి రికార్డ్ చేయండి", + narration2: + "ఇప్పుడు ఆడియోను రికార్డ్ చేయండి. మీరు మాట్లాడటం పూర్తయినప్పుడు స్టాప్ చిహ్నంపై క్లిక్ చేయండి", + narration3: "రికార్డ్ చేసిన ఆడియోను వినడానికి ప్లే బటన్‌ను క్లిక్ చేయండి", + narration4: "మళ్లీ ఆడియో రికార్డ్ చేయడానికి రీట్రై బటన్‌పై క్లిక్ చేయండి", + narration5: "కొనసాగించడానికి కంటిన్యూ క్లిక్ చేయండి", + skipDemo: "డెమోను దాటవేయండి", + startGame: "ఆట ప్రారంభించండి", + howToPlay: "ఎలా ఆడాలి", + }, + kn: { + demoSentence: "ಬೆಕ್ಕು ಮಲಗುತ್ತಿದೆ.", + narration1: + "ವಾಕ್ಯವನ್ನು ನೋಡಿ. ಬೆಕ್ಕು ಮಲಗುತ್ತಿದೆ. ಈಗ ಮೈಕ್ರೊಫೋನ್ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿ ಮತ್ತು ನೀವೇ ಹೇಳಲು ರೆಕಾರ್ಡ್ ಮಾಡಿ", + narration2: + "ಈಗ ಆಡಿಯೊವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ. ನೀವು ಮಾತನಾಡುವುದನ್ನು ಮುಗಿಸಿದಾಗ ಸ್ಟಾಪ್ ಐಕಾನ್ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration3: "ರೆಕಾರ್ಡ್ ಮಾಡಿದ ಆಡಿಯೊವನ್ನು ಕೇಳಲು ಪ್ಲೇ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಮತ್ತೆ ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು ರಿಟ್ರೈ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration5: "ಮುಂದುವರಿಸಲು ಕಂಟಿನ್ಯೂ ಕ್ಲಿಕ್ ಮಾಡಿ", + skipDemo: "ಡೆಮೊವನ್ನು ಬಿಟ್ಟುಬಿಡಿ", + startGame: "ಆಟ ಪ್ರಾರಂಭಿಸಿ", + howToPlay: "ಹೇಗೆ ಆಡಬೇಕು", + }, + mr: { + demoSentence: "मांजर झोपत आहे.", + narration1: + "वाक्य पहा. मांजर झोपत आहे. आता मायक्रोफोन बटणावर क्लिक करा आणि स्वतः म्हणण्यासाठी रेकॉर्ड करा", + narration2: + "आता ऑडिओ रेकॉर्ड करा. तुम्ही बोलणे संपवल्यावर स्टॉप आयकॉनवर क्लिक करा", + narration3: "रेकॉर्ड केलेला ऑडिओ ऐकण्यासाठी प्ले बटणावर क्लिक करा", + narration4: "पुन्हा ऑडिओ रेकॉर्ड करण्यासाठी रीट्राय बटणावर क्लिक करा", + narration5: "पुढे जाण्यासाठी कंटिन्यू क्लिक करा", + skipDemo: "डेमो वगळा", + startGame: "खेळ सुरू करा", + howToPlay: "कसे खेळायचे", + }, + hi: { + demoSentence: "बिल्ली सो रही है।", + narration1: + "वाक्य देखें। बिल्ली सो रही है। अब माइक्रोफोन बटन पर क्लिक करें और खुद कहने के लिए रिकॉर्ड करें", + narration2: + "अब ऑडियो रिकॉर्ड करें। जब आप बोलना समाप्त करें तो स्टॉप आइकन पर क्लिक करें", + narration3: "रिकॉर्ड किए गए ऑडियो को सुनने के लिए प्ले बटन पर क्लिक करें", + narration4: "फिर से ऑडियो रिकॉर्ड करने के लिए रीट्राई बटन पर क्लिक करें", + narration5: "आगे बढ़ने के लिए कंटिन्यू पर क्लिक करें", + skipDemo: "डेमो छोड़ें", + startGame: "खेल शुरू करें", + howToPlay: "कैसे खेलें", + }, + gu: { + demoSentence: "બિલાડી ઊંઘી રહી છે.", + narration1: + "વાક્ય જુઓ. બિલાડી ઊંઘી રહી છે. હવે માઇક્રોફોન બટન પર ક્લિક કરો અને તમે પોતે કહેવા માટે રેકોર્ડ કરો", + narration2: + "હવે ઓડિયો રેકોર્ડ કરો. જ્યારે તમે બોલવાનું સમાપ્ત કરો ત્યારે સ્ટોપ આઇકોન પર ક્લિક કરો", + narration3: "રેકોર્ડ કરેલ ઓડિયો સાંભળવા માટે પ્લે બટન પર ક્લિક કરો", + narration4: "ફરીથી ઓડિયો રેકોર્ડ કરવા માટે રિટ્રાય બટન પર ક્લિક કરો", + narration5: "આગળ વધવા માટે કન્ટિન્યુ પર ક્લિક કરો", + skipDemo: "ડેમો છોડો", + startGame: "રમત શરૂ કરો", + howToPlay: "કેવી રીતે રમવું", + }, + ta: { + demoSentence: "பூனை தூங்குகிறது.", + narration1: + "வாக்கியத்தைப் பார்க்கவும். பூனை தூங்குகிறது. இப்போது மைக்ரோஃபோன் பொத்தானைக் கிளிக் செய்து நீங்களே சொல்ல பதிவு செய்யவும்", + narration2: + "இப்போது ஆடியோவைப் பதிவு செய்யவும். நீங்கள் பேசி முடித்ததும் ஸ்டாப் ஐகானைக் கிளிக் செய்யவும்", + narration3: + "பதிவு செய்யப்பட்ட ஆடியோவைக் கேட்க பிளே பொத்தானைக் கிளிக் செய்யவும்", + narration4: + "மீண்டும் ஆடியோவைப் பதிவு செய்ய ரீட்ரை பொத்தானைக் கிளிக் செய்யவும்", + narration5: "தொடர கன்டினியூவைக் கிளிக் செய்யவும்", + skipDemo: "டெமோவைத் தவிர்க்கவும்", + startGame: "விளையாட்டைத் தொடங்கவும்", + howToPlay: "எப்படி விளையாடுவது", + }, + or: { + demoSentence: "ବିଲେଇ ଶୋଇଛି।", + narration1: + "ବାକ୍ୟ ଦେଖନ୍ତୁ। ବିଲେଇ ଶୋଇଛି। ବର୍ତ୍ତମାନ ମାଇକ୍ରୋଫୋନ୍ ବଟନ୍ କ୍ଲିକ୍ କରନ୍ତୁ ଏବଂ ନିଜେ କହିବା ପାଇଁ ରେକର୍ଡ କରନ୍ତୁ", + narration2: + "ବର୍ତ୍ତମାନ ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ। ଯେତେବେଳେ ଆପଣ କଥା ସମାପ୍ତ କରିବେ ଷ୍ଟପ୍ ଆଇକନ୍ କ୍ଲିକ୍ କରନ୍ତୁ", + narration3: "ରେକର୍ଡ କରାଯାଇଥିବା ଅଡିଓ ଶୁଣିବା ପାଇଁ ପ୍ଲେ ବଟନ୍ କ୍ଲିକ୍ କରନ୍ତୁ", + narration4: "ପୁନର୍ବାର ଅଡିଓ ରେକର୍ଡ କରିବାକୁ ରିଟ୍ରାଇ ବଟନ୍ କ୍ଲିକ୍ କରନ୍ତୁ", + narration5: "ଆଗକୁ ବଢିବାକୁ କଣ୍ଟିନ୍ୟୁ କ୍ଲିକ୍ କରନ୍ତୁ", + skipDemo: "ଡେମୋ ଛାଡନ୍ତୁ", + startGame: "ଖେଳ ଆରମ୍ଭ କରନ୍ତୁ", + howToPlay: "କିପରି ଖେଳିବେ", + }, +}; + +const DiscoverSentencePreview = ({ onStartGame, onBack }) => { + // Demo states: countdown, showSentence, recording, playAudio, retryOrContinue, completion + const [demoPhase, setDemoPhase] = useState("countdown"); + const [currentDemoStep, setCurrentDemoStep] = useState(1); // Track demo step progress (1-5) + const [showPointer, setShowPointer] = useState(false); + const [pointerTarget, setPointerTarget] = useState(""); // mic, stop, play, retry, continue + const [isFirstRecording, setIsFirstRecording] = useState(true); + const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); + const [isInstructionPlaying, setIsInstructionPlaying] = useState(false); + + // Button visibility states for demo + const [showSpeakButton, setShowSpeakButton] = useState(false); + const [showStopButton, setShowStopButton] = useState(false); + const [showListenRetryButtons, setShowListenRetryButtons] = useState(false); + const [isRecordingDemo, setIsRecordingDemo] = useState(false); + + // Audio playback state + const [isAudioPlaying, setIsAudioPlaying] = useState(false); + + // Audio recording states + const [recordedAudioBlob, setRecordedAudioBlob] = useState(null); + const [recordedAudioUrl, setRecordedAudioUrl] = useState(null); + const mediaRecorderRef = useRef(null); + const audioChunksRef = useRef([]); + const audioRef = useRef(null); + const instructionAudioRef = useRef(null); // Ref to store current instruction audio + + const language = getLocalData("lang") || "en"; + const instructions = demoInstructions[language] || demoInstructions.en; + + // Handle countdown complete + const handleCountdownComplete = () => { + setDemoPhase("showSentence"); + setCurrentDemoStep(1); + setShowSpeakButton(true); + setShowStopButton(false); + setShowListenRetryButtons(false); + setIsRecordingDemo(false); + + // Play first instruction after a small delay + setTimeout(async () => { + await playInstruction(instructions.narration1, 1); + setShowPointer(true); + setPointerTarget("mic"); + }, 1000); + }; + + // Play instruction audio from S3 - returns a promise that resolves when done + const playInstruction = (text, step) => { + // Stop any currently playing instruction audio + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + + setIsInstructionPlaying(true); + + return new Promise((resolve) => { + // Build S3 audio path: /audio/audio-preview/combined-sentence-games/sentence-recording/{language}/narration{step}.wav + // const audioPath = `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL || ''}/audio/audio-preview/combined-sentence-games/sentence-recording/${language}/narration${step}.wav`; + const audioPath = `/audio/audio-preview/sentence-recording/${language}/narration${step}.wav`; + const audio = new Audio(audioPath); + instructionAudioRef.current = audio; // Store audio reference + + audio.onended = () => { + setIsInstructionPlaying(false); + instructionAudioRef.current = null; + resolve(); + }; + + audio.onerror = () => { + console.error(`Audio file not found at ${audioPath}`); + setIsInstructionPlaying(false); + instructionAudioRef.current = null; + resolve(); + }; + + audio.play().catch((error) => { + console.error(`Audio playback failed: ${error}`); + setIsInstructionPlaying(false); + instructionAudioRef.current = null; + resolve(); + }); + }); + }; + + // Helper function to play multiple instructions sequentially + const playInstructionsSequentially = async (texts, steps) => { + for (let i = 0; i < texts.length; i++) { + await playInstruction(texts[i], steps[i]); + } + }; + + // Start audio recording + const startAudioRecording = useCallback(async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + const mimeType = MediaRecorder.isTypeSupported("audio/webm") + ? "audio/webm" + : "audio/mp4"; + const mediaRecorder = new MediaRecorder(stream, { mimeType }); + + audioChunksRef.current = []; + + mediaRecorder.ondataavailable = (event) => { + if (event.data.size > 0) { + audioChunksRef.current.push(event.data); + } + }; + + mediaRecorder.onstop = () => { + const audioBlob = new Blob(audioChunksRef.current, { type: mimeType }); + const audioUrl = URL.createObjectURL(audioBlob); + setRecordedAudioBlob(audioBlob); + setRecordedAudioUrl(audioUrl); + + // Stop all tracks + stream.getTracks().forEach((track) => track.stop()); + }; + + mediaRecorder.start(); + mediaRecorderRef.current = mediaRecorder; + } catch (error) { + console.error("Error accessing microphone:", error); + alert("Please allow microphone access to record audio."); + } + }, []); + + // Stop audio recording + const stopAudioRecording = useCallback(() => { + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + mediaRecorderRef.current.stop(); + } + }, []); + + // Handle microphone button click (user starts recording) + const handleMicClick = async () => { + if (demoPhase !== "showSentence") return; + if (isInstructionPlaying) return; // Don't allow clicks while instruction is playing + + setShowPointer(false); + setShowSpeakButton(false); + setShowStopButton(true); + setDemoPhase("recording"); + setCurrentDemoStep(2); + + // Play instruction only on first recording, then start recording + if (isFirstRecording) { + // Play instruction first + await new Promise((resolve) => { + setTimeout(async () => { + await playInstruction(instructions.narration2, 2); + resolve(); + }, 500); + }); + + // After instruction completes, start recording + setIsRecordingDemo(true); + startAudioRecording(); + + setTimeout(() => { + setShowPointer(true); + setPointerTarget("stop"); + }, 300); + } else { + // No instruction on retry, start recording immediately + setIsRecordingDemo(true); + startAudioRecording(); + + setTimeout(() => { + setShowPointer(true); + setPointerTarget("stop"); + }, 500); + } + }; + + // Handle stop button click (user stops recording) + const handleStopClick = async () => { + if (demoPhase !== "recording") return; + if (isInstructionPlaying) return; // Don't allow clicks while instruction is playing + + setShowPointer(false); + setShowStopButton(false); + setShowListenRetryButtons(true); + setIsRecordingDemo(false); + setDemoPhase("playAudio"); + setIsFirstRecording(false); + setCurrentDemoStep(3); + + // Stop actual audio recording + stopAudioRecording(); + + // Play instruction to click play button - wait for it to complete + setTimeout(async () => { + await playInstruction(instructions.narration3, 3); + + // Only show pointer and enable button after instruction completes + setShowPointer(true); + setPointerTarget("play"); + }, 500); + }; + + // Handle play/listen button click (user listens to recording) + const handlePlayClick = async () => { + if (demoPhase !== "playAudio") return; + + // Don't allow clicking if instruction is still playing + if (isInstructionPlaying) { + console.log("Please wait for instruction to finish"); + return; + } + + setShowPointer(false); + + // Play the actual recorded audio + if (recordedAudioUrl && audioRef.current) { + audioRef.current.src = recordedAudioUrl; + audioRef.current.play(); + setIsAudioPlaying(true); + + audioRef.current.onended = async () => { + setIsAudioPlaying(false); + // After playback completes, move to retry/continue phase + setDemoPhase("retryOrContinue"); + setCurrentDemoStep(4); + // Show instruction for retry or continue with pointer to retry button + // Play narration4 and narration5 sequentially + setTimeout(async () => { + await playInstructionsSequentially( + [instructions.narration4, instructions.narration5], + [4, 5] + ); + setShowPointer(true); + setPointerTarget("retry"); + }, 500); + }; + + audioRef.current.onerror = () => { + setIsAudioPlaying(false); + setDemoPhase("retryOrContinue"); + setCurrentDemoStep(4); + }; + } else { + // If no recording exists (shouldn't happen), move to next phase + setDemoPhase("retryOrContinue"); + setCurrentDemoStep(4); + setTimeout(async () => { + // Play narration4 and narration5 sequentially + await playInstructionsSequentially( + [instructions.narration4, instructions.narration5], + [4, 5] + ); + setShowPointer(true); + setPointerTarget("retry"); + }, 500); + } + }; + + // Handle retry button click (user wants to record again) + const handleRetryClick = () => { + if (demoPhase !== "retryOrContinue") return; + + // Don't allow clicking if instruction is still playing + if (isInstructionPlaying) { + console.log("Please wait for instruction to finish"); + return; + } + + setShowPointer(false); + setIsAudioPlaying(false); + setShowListenRetryButtons(false); + setShowSpeakButton(true); + setDemoPhase("showSentence"); + setCurrentDemoStep(1); + // Clear previous recording + if (recordedAudioUrl) { + URL.revokeObjectURL(recordedAudioUrl); + } + setRecordedAudioBlob(null); + setRecordedAudioUrl(null); + + // Go back to microphone step, but no instruction this time + setTimeout(() => { + setShowPointer(true); + setPointerTarget("mic"); + }, 500); + }; + + // Handle continue button click (user proceeds to completion screen) + const handleContinueClick = () => { + if (demoPhase !== "retryOrContinue") return; + + // Don't allow clicking if instruction is still playing + if (isInstructionPlaying) { + console.log("Please wait for instruction to finish"); + return; + } + + setShowPointer(false); + setDemoPhase("completion"); + }; + + // Handle start game from completion screen + const handleStartGameClick = () => { + // Stop instruction audio if it's playing + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + setIsInstructionPlaying(false); + setIsAudioPlaying(false); + + // Stop recorded audio if it's playing + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + // Clean up audio recording + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + mediaRecorderRef.current.stop(); + } + if (recordedAudioUrl) { + URL.revokeObjectURL(recordedAudioUrl); + } + + onStartGame(); + }; + + // Handle replay demo from completion screen + const handleReplayDemo = () => { + // Stop recorded audio if it's playing + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + // Clean up audio recording + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + mediaRecorderRef.current.stop(); + } + if (recordedAudioUrl) { + URL.revokeObjectURL(recordedAudioUrl); + } + setRecordedAudioBlob(null); + setRecordedAudioUrl(null); + + // Reset to countdown + setDemoPhase("countdown"); + setShowPointer(false); + setIsFirstRecording(true); + setIsAudioPlaying(false); + }; + + // Dummy handleNext for demo + const handleNext = () => { + // This is called by WordsOrImage but we handle it with our custom handlers + }; + + const handleBackClick = () => { + // Stop instruction audio if it's playing + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + setIsInstructionPlaying(false); + setIsAudioPlaying(false); + + // Stop recorded audio if it's playing + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + onBack(); + }; + + const handleSkipDemo = () => { + // Stop instruction audio if it's playing + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + setIsInstructionPlaying(false); + setIsAudioPlaying(false); + + // Stop recorded audio if it's playing + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + + // Clean up audio recording + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state !== "inactive" + ) { + mediaRecorderRef.current.stop(); + } + if (recordedAudioUrl) { + URL.revokeObjectURL(recordedAudioUrl); + } + + onStartGame(); + }; + + // Completion messages + const completionMessages = { + en: { + title: "Demo Complete!", + message: "Great job! You're ready to start.", + replayText: "Replay", + continueText: "Continue", + }, + te: { + title: "డెమో పూర్తయింది!", + message: "బాగా చేసారు! మీరు ప్రారంభించడానికి సిద్ధంగా ఉన్నారు.", + replayText: "మళ్లీ ప్లే చేయండి", + continueText: "కొనసాగించు", + }, + kn: { + title: "ಡೆಮೊ ಪೂರ್ಣಗೊಂಡಿದೆ!", + message: "ಅದ್ಭುತ! ನೀವು ಪ್ರಾರಂಭಿಸಲು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ.", + replayText: "ಮರುಪ್ಲೇ", + continueText: "ಮುಂದುವರಿಸಿ", + }, + mr: { + title: "डेमो पूर्ण झाला!", + message: "छान केलं! तुम्ही सुरू करण्यास तयार आहात.", + replayText: "पुन्हा प्ले करा", + continueText: "पुढे चला", + }, + hi: { + title: "डेमो पूर्ण हुआ!", + message: "बहुत बढ़िया! आप शुरू करने के लिए तैयार हैं।", + replayText: "फिर से प्ले करें", + continueText: "जारी रखें", + }, + gu: { + title: "ડેમો પૂર્ણ થયું!", + message: "ખૂબ સરસ! તમે શરૂ કરવા તૈયાર છો.", + replayText: "ફરીથી પ્લે કરો", + continueText: "ચાલુ રાખો", + }, + ta: { + title: "டெமோ முடிந்தது!", + message: "நன்றாக செய்தீர்கள்! நீங்கள் தொடங்க தயாராக உள்ளீர்கள்.", + replayText: "மீண்டும் இயக்கு", + continueText: "தொடர்க", + }, + or: { + title: "ଡେମୋ ସମ୍ପୂର୍ଣ୍ଣ!", + message: "ବହୁତ ଭଲ! ଆପଣ ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ।", + replayText: "ପୁନର୍ବାର ପ୍ଲେ କରନ୍ତୁ", + continueText: "ଜାରି ରଖନ୍ତୁ", + }, + }; + + const messages = completionMessages[language] || completionMessages.en; + + // Render discovery UI with interactive demo + return ( +
+ {/* Always render WordsOrImage (discovery UI) */} + {}} + setRecordedAudio={() => {}} + setVoiceAnimate={() => {}} + storyLine={0} + handleNext={handleNext} + type="text" + enableNext={false} + showTimer={false} + points={0} + steps={1} + currentStep={1} + isDiscover={true} + callUpdateLearner={false} + disableScreen={demoPhase === "countdown"} + handleBack={handleBackClick} + setEnableNext={() => {}} + isNextButtonCalled={isNextButtonCalled} + setIsNextButtonCalled={setIsNextButtonCalled} + setOpenMessageDialog={() => {}} + startShowCase={true} + isDemo={true} + showSpeakButton={showSpeakButton} + showStopButton={showStopButton} + showListenRetryButtons={showListenRetryButtons} + isRecording={isRecordingDemo} + isAudioPlaying={isAudioPlaying} + onMicClick={handleMicClick} + onStopClick={handleStopClick} + onPlayClick={handlePlayClick} + onRetryClick={handleRetryClick} + onNextClick={handleContinueClick} + isInstructionPlaying={isInstructionPlaying} + showPointer={ + showPointer && demoPhase !== "countdown" && !isInstructionPlaying + } + pointerTarget={pointerTarget} + /> + + {/* Countdown Timer Overlay - Only during countdown */} + {demoPhase === "countdown" && ( +
+ +
+ )} + + {/* Demo Completion Floating Card */} + {demoPhase === "completion" && ( +
+
+
🎉
+

+ {messages.title} +

+

+ {messages.message} +

+
+
{ + e.currentTarget.style.transform = "scale(1.1)"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = "scale(1)"; + }} + > + + + {/* {messages.replayText} */} + +
+
{ + e.currentTarget.style.transform = "scale(1.1)"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = "scale(1)"; + }} + > + + + {/* {messages.continueText} */} + +
+
+
+
+ )} + + {/* "How to Play" Progress Indicator */} + {(demoPhase === "showSentence" || + demoPhase === "recording" || + demoPhase === "playAudio" || + demoPhase === "retryOrContinue") && ( +
+
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+
+ )} + + {/* Demo Control Buttons - Inside White Container */} + {(demoPhase === "showSentence" || + demoPhase === "recording" || + demoPhase === "playAudio" || + demoPhase === "retryOrContinue") && ( +
+ {/* Skip Demo Button */} + + + {/* Start Game Button */} + +
+ )} + + {/* Hidden audio element for playback */} +
+ ); +}; + +export default DiscoverSentencePreview; diff --git a/src/components/Layouts.jsx/MainLayout.jsx b/src/components/Layouts.jsx/MainLayout.jsx index 23729f25..13a38884 100644 --- a/src/components/Layouts.jsx/MainLayout.jsx +++ b/src/components/Layouts.jsx/MainLayout.jsx @@ -40,6 +40,7 @@ import { callConfettiSnow, levelConfig, practiceSteps, + levelGetContent, getLocalData, LevelTen, LevelEleven, @@ -49,7 +50,6 @@ import { LevelFifteen, ROneImg, setLocalData, - LevelBeginner, } from "../../utils/constants"; import { ChevronLeft, ChevronRight } from "@mui/icons-material"; import { IconButton } from "@mui/material"; @@ -58,7 +58,7 @@ import Confetti from "react-confetti"; import LevelCompleteAudio from "../../assets/audio/levelComplete.wav"; import gameLoseAudio from "../../assets/audio/gameLose.wav"; import * as Assets from "../../utils/imageAudioLinks"; -import { useEffect, useState, useRef } from "react"; +import { useEffect, useState, useRef, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { levelMapping } from "../../utils/levelData"; import { jwtDecode } from "jwt-decode"; @@ -67,18 +67,25 @@ import rOneImg from "../../assets/R1.png"; import rTwoMileImage from "../../assets/r2mile.png"; import rThreeMileImage from "../../assets/r3mile.png"; import rFourMileImage from "../../assets/r4mile.png"; +import F1Image from "../../assets/F1.png"; +import F2Image from "../../assets/F2.png"; +import F3Image from "../../assets/F3.png"; import zIndex from "@mui/material/styles/zIndex"; import { Log } from "../../services/telementryService"; +import { getF1FlowStep, F1_FLOW } from "../../RFlow/F1"; +import { getF2FlowStep, F2_FLOW } from "../../RFlow/F2"; +import { getF3FlowStep, F3_FLOW } from "../../RFlow/F3"; const theme = createTheme(); const MainLayout = (props) => { + // console.log("MainLayout props:", props); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); const levelsImages = { B: { - milestone: , + milestone: F1, backgroundAddOn: practicebgstone, background: practicebg, backgroundColor: `#FFB31F`, @@ -171,19 +178,162 @@ const MainLayout = (props) => { backgroundColor: `${levelConfig[9].color}60`, }, }; - + const hasTriggeredDemoRef = useRef(false); const rFlow = String(getLocalData("rFlow")); const rStep = getLocalData("rStepZero"); const tFlow = String(getLocalData("tFlow")); const mFlow = getLocalData("mFail"); const allCompleted = getLocalData("allCompleted"); - console.log("rStep", rStep); + // Get milestone_level from API to determine if F1 flow should be active + const getMilestoneData = () => { + try { + const milestoneStr = getLocalData("getMilestone"); + if (milestoneStr) { + return JSON.parse(milestoneStr); + } + } catch (e) { + console.error("Error parsing getMilestone:", e); + } + return null; + }; + const milestoneData = getMilestoneData(); + const milestoneLevel = milestoneData?.data?.milestone_level || null; + const subMilestoneLevel = milestoneData?.data?.sub_milestone_level || null; + + // F1 flow is triggered when milestone_level is "B" and sub_milestone_level is "F1" + const shouldShowF1 = milestoneLevel === "B" && subMilestoneLevel === "F1"; + // F2 flow is triggered when milestone_level is "B" and sub_milestone_level is "F2" + const shouldShowF2 = milestoneLevel === "B" && subMilestoneLevel === "F2"; + // F3 flow is triggered when milestone_level is "B" and sub_milestone_level is "F3" + const shouldShowF3 = milestoneLevel === "B" && subMilestoneLevel === "F3"; + + // Check if F1 flow is active + const f1FlowStep = getF1FlowStep(); + const isF1FlowActive = shouldShowF1 && f1FlowStep.step !== null; + + // Check if F2 flow is active + const f2FlowStep = getF2FlowStep(); + const isF2FlowActive = shouldShowF2 && f2FlowStep.step !== null; + + // Check if F3 flow is active + const f3FlowStep = getF3FlowStep(); + const isF3FlowActive = shouldShowF3 && f3FlowStep.step !== null; + + // console.log("rStep", rStep); let LEVEL = props?.level; - let flowNames = props?.flowNames; + // If milestone level is "m1", "m2", etc., extract the number for image lookup + // This ensures we show the correct milestone level image instead of F flow images + // Priority: milestone level > props.level + if ( + milestoneLevel && + typeof milestoneLevel === "string" && + milestoneLevel.startsWith("m") + ) { + const milestoneNumber = parseInt(milestoneLevel.substring(1), 10); + if (!isNaN(milestoneNumber)) { + LEVEL = milestoneNumber; + } + } else if (milestoneLevel && milestoneLevel !== "B") { + // If milestone level is not "B" and not "m1", "m2", etc., try to extract number anyway + // This handles cases where milestone level might be just a number string + const milestoneNumber = parseInt(milestoneLevel, 10); + if (!isNaN(milestoneNumber)) { + LEVEL = milestoneNumber; + } + } + + // Use F2 step names if F2 flow is active, otherwise F1, otherwise props flowNames + // flowNames should be ["L1", "P1", "L2", "P2", "L3", "P3", "A1", ...] for F1/F2 flow + const getF1FlowNames = () => { + if (!isF1FlowActive) return null; + return F1_FLOW.map((flowStep) => { + if (flowStep.type === "L") { + return `L${flowStep.step}`; + } else if (flowStep.type === "P") { + return `P${flowStep.step}`; + } else if (flowStep.type === "A") { + return `A${flowStep.step}`; + } + return ""; + }); + }; + + const getF2FlowNames = () => { + if (!isF2FlowActive) return null; + return F2_FLOW.map((flowStep) => { + if (flowStep.type === "L") { + return `L${flowStep.step}`; + } else if (flowStep.type === "P") { + return `P${flowStep.step}`; + } else if (flowStep.type === "A") { + return `A${flowStep.step}`; + } + return ""; + }); + }; + + const getF3FlowNames = () => { + if (!isF3FlowActive) return null; + return F3_FLOW.map((flowStep) => { + if (flowStep.type === "L") { + return `L${flowStep.step}`; + } else if (flowStep.type === "P") { + return `P${flowStep.step}`; + } else if (flowStep.type === "A") { + return `A${flowStep.step}`; + } + return ""; + }); + }; + + let flowNames = isF3FlowActive + ? getF3FlowNames() || props?.flowNames + : isF2FlowActive + ? getF2FlowNames() || props?.flowNames + : isF1FlowActive + ? getF1FlowNames() || props?.flowNames + : props?.flowNames; + + // For F3 flow, set activeFlow based on current F3 step + // For F2 flow, set activeFlow based on current F2 step + // For F1 flow, set activeFlow based on current F1 step + // activeFlow should be P1, P2, A1, etc. based on F3_FLOW, F2_FLOW, or F1_FLOW let activeFlow = props?.activeFlow; + if (isF3FlowActive && f3FlowStep.step) { + const currentFlowStep = F3_FLOW[f3FlowStep.index]; + if (currentFlowStep) { + if (currentFlowStep.type === "P") { + activeFlow = `P${currentFlowStep.step}`; // P1, P2, P3, etc. + } else if (currentFlowStep.type === "A") { + activeFlow = `A${currentFlowStep.step}`; // A1, A2 + } + } + } else if (isF2FlowActive && f2FlowStep.step) { + const currentFlowStep = F2_FLOW[f2FlowStep.index]; + if (currentFlowStep) { + if (currentFlowStep.type === "L") { + activeFlow = `L${currentFlowStep.step}`; // L1, L2, L3, etc. + } else if (currentFlowStep.type === "P") { + activeFlow = `P${currentFlowStep.step}`; // P1, P2, P3, etc. + } else if (currentFlowStep.type === "A") { + activeFlow = `A${currentFlowStep.step}`; // A1, A2, A3 + } + } + } else if (isF1FlowActive && f1FlowStep.step) { + const currentFlowStep = F1_FLOW[f1FlowStep.index]; + if (currentFlowStep) { + if (currentFlowStep.type === "L") { + activeFlow = `L${currentFlowStep.step}`; // L1, L2, L3, etc. + } else if (currentFlowStep.type === "P") { + activeFlow = `P${currentFlowStep.step}`; // P1, P2, P3, etc. + } else if (currentFlowStep.type === "A") { + activeFlow = `A${currentFlowStep.step}`; // A1, A2, A3 + } + } + } const virtualId = String(getLocalData("virtualId")); @@ -247,7 +397,7 @@ const MainLayout = (props) => { const language = getLocalData("lang"); - console.log("levelss", LEVEL, livesData); + //console.log("levelss", LEVEL, livesData); // useEffect(() => { // if (language !== "en") { @@ -352,11 +502,338 @@ const MainLayout = (props) => { }; } }, [startShowCase, isShowCase, gameOverData, audioCache]); + // console.log(" MainLayout gameOverData", gameOverData); + // console.log(" MainLayout gameOverData userWon", gameOverData?.userWon); + // console.log(" MainLayout LEVEL", LEVEL); + // console.log(" MainLayout progressData", props); + // console.log( + // " MainLayout progressData prop", + // props?.progressData?.currentPracticeStep + // ); + + useEffect(() => { + if (hasTriggeredDemoRef.current) return; + + // ❌ wait until gameOverData is available + if (!gameOverData) return; + + const userDidNotWin = gameOverData.userWon !== true; + const isValidLevel = [1, 2, 3].includes(LEVEL); + + // Determine if demo should trigger based on flow type: + // - F1 flow active: only trigger for L1 (index 0) — the only IMMEDIATE milestone + // A1/A2/A3 are DEFERRED and only trigger from Start Game button click + // - Non-F1 flow (regular): trigger when user fails at S2 (step 9) + let shouldTrigger = false; + if (isF1FlowActive) { + const immediateMilestones = [0]; // Only L1 + const currentF1Index = Number(getLocalData("f1FlowIndex") || -1); + shouldTrigger = immediateMilestones.includes(currentF1Index); + } else { + // Regular flow: S2 fail scenario (step 9) + const isStepNine = props?.progressData?.currentPracticeStep === 9; + shouldTrigger = isStepNine; + } + + if (userDidNotWin && isValidLevel && shouldTrigger) { + hasTriggeredDemoRef.current = true; + + setLocalData("showAlphabetDemo", "true"); + // console.log("Triggering alphabet demo"); + + window.dispatchEvent(new Event("alphabetDemoComplete")); + } + }, [ + gameOverData, + LEVEL, + props?.progressData?.currentPracticeStep, + isF1FlowActive, + ]); let currentPracticeStep = progressData?.currentPracticeStep; + // For F1/F2/F3 flow, use the flow index instead of currentPracticeStep + if (isF3FlowActive && f3FlowStep.index !== undefined) { + currentPracticeStep = f3FlowStep.index; + } else if (isF2FlowActive && f2FlowStep.index !== undefined) { + currentPracticeStep = f2FlowStep.index; + } else if (isF1FlowActive && f1FlowStep.index !== undefined) { + currentPracticeStep = f1FlowStep.index; + } const [currentPageStart, setCurrentPageStart] = useState(0); const prevActiveFlow = useRef(null); + // State for progress bar pagination (dynamic steps based on width) + const [progressBarStartIndex, setProgressBarStartIndex] = useState(0); + const progressBarContainerRef = useRef(null); + const progressBarParentRef = useRef(null); + const [containerWidth, setContainerWidth] = useState(0); + + // Calculate how many steps can fit based on available width + // This is calculated dynamically based on actual container width + const calculateVisibleSteps = useMemo(() => { + if (containerWidth === 0) return 5; // Default to 5 if width not measured yet + + // Step dimensions based on screen size + const stepWidth = isMobile ? 28 : isTablet ? 32 : 36; // xs: 28px, sm: 32px, md: 36px, lg: 40px + const stepMargin = isMobile ? 4 : isTablet ? 8 : 12; // xs: 0.5 * 8px, sm: 1 * 8px, md: 1.5 * 8px + const containerPadding = isMobile ? 16 : isTablet ? 24 : 32; // xs: 8px*2, sm: 12px*2, md: 16px*2 + const buttonWidth = isMobile ? 40 : isTablet ? 48 : 56; // Button width + const buttonGap = isMobile ? 8 : 12; // Gap between button and container + + // Account for left and right margins (180px mobile, 200px tablet, 220px desktop) + const leftMargin = isMobile ? 180 : isTablet ? 200 : 220; + const rightMargin = isMobile ? 180 : isTablet ? 200 : 220; + + // The containerWidth is the width of the progress bar container (after margins) + // But we need to calculate based on the actual available space + // Available width = containerWidth (which already accounts for margins) - padding - button space + const reservedForButtons = buttonWidth * 2 + buttonGap * 2; + + // Available width for steps = container width - padding - reserved button space + const availableWidth = + containerWidth - containerPadding - reservedForButtons; + + // Calculate how many steps can fit: (availableWidth + stepMargin) / (stepWidth + stepMargin) + // We add stepMargin to availableWidth because the first step doesn't have left margin + const stepsThatFit = Math.floor( + (availableWidth + stepMargin) / (stepWidth + stepMargin) + ); + + // Be more generous - if we have space, use it! + // Minimum 5 steps, maximum 25 steps (very wide screens) + const calculatedSteps = Math.max(5, Math.min(stepsThatFit, 25)); + + return calculatedSteps; + }, [containerWidth, isMobile, isTablet]); + + const VISIBLE_STEPS = calculateVisibleSteps; + + // Get F1 steps for progress bar when F1 flow is active + // Labels should be: L1, P1, L2, P2, L3, P3, A1, L4, P4, etc. + const getF1PracticeSteps = () => { + if (!isF1FlowActive) return null; + // Use F1_FLOW to generate labels based on type and step number + return F1_FLOW.map((flowStep, index) => { + let label = ""; + if (flowStep.type === "L") { + label = `L${flowStep.step}`; // Learn: L1, L2, L3, etc. + } else if (flowStep.type === "P") { + label = `P${flowStep.step}`; // Practice: P1, P2, P3, etc. + } else if (flowStep.type === "A") { + label = `A${flowStep.step}`; // Apply: A1, A2, A3 + } + return { + name: label, + title: label, + titleNew: label, + titleThree: label, + }; + }); + }; + + const getF2PracticeSteps = () => { + if (!isF2FlowActive) return null; + // Use F2_FLOW to generate labels based on type and step number + return F2_FLOW.map((flowStep, index) => { + let label = ""; + if (flowStep.type === "L") { + label = `L${flowStep.step}`; // Learn: L1, L2, L3, etc. + } else if (flowStep.type === "P") { + label = `P${flowStep.step}`; // Practice: P1, P2, P3, etc. + } else if (flowStep.type === "A") { + label = `A${flowStep.step}`; // Apply: A1, A2, A3 + } + return { + name: label, + title: label, + titleNew: label, + titleThree: label, + }; + }); + }; + + const getF3PracticeSteps = () => { + if (!isF3FlowActive) return null; + // Use F3_FLOW to generate labels based on type and step number + return F3_FLOW.map((flowStep, index) => { + let label = ""; + if (flowStep.type === "L") { + label = `L${flowStep.step}`; // Learn: L1, L2, L3, etc. + } else if (flowStep.type === "P") { + label = `P${flowStep.step}`; // Practice: P1, P2, P3, etc. + } else if (flowStep.type === "A") { + label = `A${flowStep.step}`; // Apply: A1, A2 + } + return { + name: label, + title: label, + titleNew: label, + titleThree: label, + }; + }); + }; + + // Use F3 steps if F3 flow is active, otherwise F2 steps, otherwise F1 steps, otherwise regular practiceSteps + const displayPracticeSteps = isF3FlowActive + ? getF3PracticeSteps() || practiceSteps + : isF2FlowActive + ? getF2PracticeSteps() || practiceSteps + : isF1FlowActive + ? getF1PracticeSteps() || practiceSteps + : practiceSteps; + + // Calculate visible steps range (show dynamic steps based on width, ensure current step is visible) + const totalSteps = displayPracticeSteps?.length || 0; + + // Calculate visible range ensuring current step is always visible + const calculateVisibleRange = () => { + if (totalSteps <= VISIBLE_STEPS) { + // If total steps <= 5, show all + return { start: 0, end: totalSteps }; + } + + // Ensure current step is always visible + let start = progressBarStartIndex; + let end = Math.min(start + VISIBLE_STEPS, totalSteps); + + // If current step is not in visible range, adjust to include it + if (currentPracticeStep < start) { + start = Math.max(0, currentPracticeStep - 2); // Show 2 steps before current + end = Math.min(start + VISIBLE_STEPS, totalSteps); + } else if (currentPracticeStep >= end) { + end = Math.min(currentPracticeStep + 3, totalSteps); // Show 2 steps after current + start = Math.max(0, end - VISIBLE_STEPS); + } + + return { start, end }; + }; + + const visibleRange = calculateVisibleRange(); + const visibleSteps = + displayPracticeSteps?.slice(visibleRange.start, visibleRange.end) || []; + const canGoPrev = visibleRange.start > 0; + const canGoNext = visibleRange.end < totalSteps; + + // Measure container width on mount, resize, and when dependencies change + useEffect(() => { + if (!showProgress) return; + + const measureWidth = () => { + // Measure the parent container width (before margins are applied) + if (progressBarParentRef.current) { + requestAnimationFrame(() => { + if (progressBarParentRef.current) { + const parentWidth = progressBarParentRef.current.offsetWidth; + // Account for margins: subtract left and right margins + const leftMargin = isMobile ? 180 : isTablet ? 200 : 220; + const rightMargin = isMobile ? 180 : isTablet ? 200 : 220; + const actualWidth = parentWidth - leftMargin - rightMargin; + + if (actualWidth > 0) { + setContainerWidth(actualWidth); + } + } + }); + } else if (progressBarContainerRef.current) { + // Fallback: measure the container itself + requestAnimationFrame(() => { + if (progressBarContainerRef.current) { + const width = progressBarContainerRef.current.offsetWidth; + if (width > 0) { + setContainerWidth(width); + } + } + }); + } + }; + + // Measure after a short delay to ensure layout is complete + const timeoutId = setTimeout(measureWidth, 200); + + // Measure on window resize with debounce + let resizeTimeout; + const handleResize = () => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(measureWidth, 150); + }; + + window.addEventListener("resize", handleResize); + + return () => { + clearTimeout(timeoutId); + clearTimeout(resizeTimeout); + window.removeEventListener("resize", handleResize); + }; + }, [ + showProgress, + milestoneLevel, + isF1FlowActive, + isF2FlowActive, + isF3FlowActive, + ]); + + // Also measure when the container ref becomes available using ResizeObserver + useEffect(() => { + if (!showProgress || !progressBarContainerRef.current) return; + + const observer = new ResizeObserver((entries) => { + for (const entry of entries) { + const width = entry.contentRect.width; + if (width > 0) { + setContainerWidth(width); + } + } + }); + + observer.observe(progressBarContainerRef.current); + + return () => { + observer.disconnect(); + }; + }, [showProgress]); + + // Update progress bar start index when current step changes to keep it visible + useEffect(() => { + if (totalSteps <= VISIBLE_STEPS) { + setProgressBarStartIndex(0); + return; + } + + // Calculate if current step is in the current visible range + const currentStart = progressBarStartIndex; + const currentEnd = Math.min(currentStart + VISIBLE_STEPS, totalSteps); + + // If current step is outside visible range, adjust to center it + if ( + currentPracticeStep < currentStart || + currentPracticeStep >= currentEnd + ) { + // Center current step: show 2 before and 2 after (or adjust if near edges) + let newStart; + if (currentPracticeStep < 2) { + newStart = 0; + } else if (currentPracticeStep >= totalSteps - 2) { + newStart = Math.max(0, totalSteps - VISIBLE_STEPS); + } else { + newStart = Math.max(0, currentPracticeStep - 2); + } + setProgressBarStartIndex(newStart); + } + }, [currentPracticeStep, totalSteps, VISIBLE_STEPS]); + + const handleProgressBarPrev = () => { + const newStart = Math.max(0, progressBarStartIndex - VISIBLE_STEPS); + setProgressBarStartIndex(newStart); + }; + + const handleProgressBarNext = () => { + const newStart = Math.min( + totalSteps - VISIBLE_STEPS, + progressBarStartIndex + VISIBLE_STEPS + ); + setProgressBarStartIndex(newStart); + }; + useEffect(() => { if (!flowNames || !activeFlow) return; @@ -392,6 +869,9 @@ const MainLayout = (props) => { backgroundPosition: "center center", backgroundRepeat: "no-repeat", minHeight: "100vh", + // height: "100vh", + // maxHeight: "100vh", + // overflow: "hidden", display: "flex", paddingTop: { md: "0px", xs: "20px" }, justifyContent: "center", @@ -402,7 +882,11 @@ const MainLayout = (props) => { }; const steps = props.steps; + // console.log("steps:", steps); + const currentStep = props.currentStep; + // console.log("currentStep:", currentStep); + const stepsArr = [...Array(steps || 0).keys()]; let width = window.innerWidth * 0.85; @@ -512,6 +996,8 @@ const MainLayout = (props) => { left: { xs: "auto", md: "auto" }, width: { xs: "100%", md: "85vw" }, minHeight: "80vh", + // maxHeight: "calc(100vh - 150px)", + // height: "calc(100vh - 150px)", borderRadius: "20px", display: "flex", flexDirection: "column", @@ -522,6 +1008,7 @@ const MainLayout = (props) => { boxShadow: "0px 4px 20px -1px rgba(0, 0, 0, 0.00)", backdropFilter: "blur(25px)", mt: "75px", + // overflow: "hidden", }} > @@ -532,8 +1019,15 @@ const MainLayout = (props) => { {showTimer && ( @@ -547,32 +1041,36 @@ const MainLayout = (props) => { )} {props.children} - {steps > 0 && ( - - {stepsArr?.map((step, index) => { - const showGreen = step + 1 <= currentStep; - return ( - - ); - })} - - )} + {steps > 0 && + tFlow !== "true" && + !isF1FlowActive && + !isF2FlowActive && + !isF3FlowActive && ( + + {stepsArr?.map((step, index) => { + const showGreen = step + 1 <= currentStep; + return ( + + ); + })} + + )} {contentType && contentType.toLowerCase() !== "word" && startShowCase && ( @@ -621,35 +1119,80 @@ const MainLayout = (props) => { }} >
- {rFlow === "true" ? ( - [1, "B"]?.includes(LEVEL) ? ( - R One - ) : LEVEL === 2 ? ( - {`R - ) : null - ) : ( - LEVEL && levelsImages?.[LEVEL]?.milestone - )} + {/* Debug: Log milestone level and LEVEL for troubleshooting */} + {/* {console.log( + "MainLayout footer - milestoneLevel:", + milestoneLevel, + "LEVEL:", + LEVEL, + "rFlow:", + rFlow + )} */} + + {/* Only show F flow images when milestone level is "B" */} + {tFlow !== "true" && + (milestoneLevel === "B" && isF3FlowActive ? ( + // F3 Flow - Show F3 milestone image +
+ F3 +
+ ) : milestoneLevel === "B" && isF2FlowActive ? ( + // F2 Flow - Show F2 milestone image +
+ F2 +
+ ) : milestoneLevel === "B" && isF1FlowActive ? ( + // F1 Flow - Show F1 milestone image +
+ F1 +
+ ) : rFlow === "true" && milestoneLevel === "B" ? ( + // Only show R flow images when milestone level is "B" + [1, "B"]?.includes(LEVEL) ? ( + // R0 - Show F1 milestone image instead of R0 image + rStep == null || rStep === 0 || rStep === "0" ? ( + F1 + ) : ( + R One + ) + ) : LEVEL === 2 ? ( + {`R + ) : null + ) : ( + LEVEL && levelsImages?.[LEVEL]?.milestone + ))}
{ width: "100%", }} > - {showNext && ( - - {showProgress && rFlow !== "true" && ( - + {/* Show displayPracticeSteps progress bar - hide when flowNames progress bar is showing */} + {/* Show progress bar for F1, F2, or when conditions are met */} + {(showNext || showProgress) && + tFlow !== "true" && + !( + rFlow === "true" && + ![1, "B", 3]?.includes(LEVEL) && + !isF1FlowActive && + !isF2FlowActive && + !isF3FlowActive + ) && ( + + {/* Show progress bar - use F2 flow steps when F2 is active, F1 flow steps when F1 is active, otherwise use regular steps */} + {/* Show dynamic steps based on available width with prev/next buttons */} + {showProgress && ( - {" "} + {/* Previous Button */} + {canGoPrev && ( + + + + )} + + {/* Progress Steps Container */} { alignItems: "center", height: "48px", border: "1.5px solid rgba(51, 63, 97, 0.15)", - ml: { - xs: 10, - sm: 15, - lg: 25, - md: 18, - }, borderRadius: "30px", background: "white", + padding: { + xs: "4px 8px", + sm: "4px 12px", + md: "4px 16px", + }, + minWidth: { + xs: "200px", + sm: "280px", + md: "320px", + }, + flex: 1, + maxWidth: "100%", }} > - {practiceSteps.map((elem, i) => { + {visibleSteps.map((elem, visibleIndex) => { + const actualIndex = + visibleRange.start + visibleIndex; return ( i + currentPracticeStep > actualIndex ? "linear-gradient(90deg, rgba(132, 246, 48, 0.1) 0%, rgba(64, 149, 0, 0.1) 95%)" - : currentPracticeStep === i + : currentPracticeStep === actualIndex ? "linear-gradient(90deg, #FF4BC2 0%, #C20281 95%)" : "rgba(0, 0, 0, 0.04)", - ml: { - xs: 0.5, - sm: 0.5, - md: 1.5, - lg: 2, - }, - mr: - i === practiceSteps?.length - 1 ? 2 : 0, - borderRadius: "30px", + ml: + visibleIndex > 0 + ? { xs: 0.5, sm: 1, md: 1.5 } + : 0, + borderRadius: "50%", display: "flex", justifyContent: "center", alignItems: "center", + flexShrink: 0, }} > - {currentPracticeStep > i ? ( + {currentPracticeStep > actualIndex ? ( ) : ( @@ -760,7 +1375,7 @@ const MainLayout = (props) => { : LEVEL === 2 ? elem.titleNew : LEVEL === 3 - ? elem.titleThree + ? elem.titleNew : elem.name} )} @@ -768,144 +1383,187 @@ const MainLayout = (props) => { ); })} + + {/* Next Button */} + {canGoNext && ( + + + + )} - - )} - {rFlow === "true" && ![1, "B"]?.includes(LEVEL) && ( - - + )} + {/* Hide flowNames progress bar when F1, F2, F3, or M3 flow is active - use displayPracticeSteps instead */} + {/* M3 (LEVEL 3) should use displayPracticeSteps, not flowNames */} + {rFlow === "true" && + ![1, "B", 3]?.includes(LEVEL) && + !isF1FlowActive && + !isF2FlowActive && + !isF3FlowActive && ( - - - - - {flowNames - ?.slice( - currentPageStart, - currentPageStart + 10 - ) - .map((flow, i) => ( - - {flowNames?.indexOf(flow) < - flowNames?.indexOf(activeFlow) ? ( - - ) : ( - + + + + + + {flowNames + ?.slice( + currentPageStart, + currentPageStart + 10 + ) + .map((flow, i) => ( + - {flow} - - )} - - ))} - + {flowNames?.indexOf(flow) < + flowNames?.indexOf(activeFlow) ? ( + + ) : ( + + {flow} + + )} + + ))} + - = flowNames?.length - } - sx={{ - ml: 1, - visibility: - currentPageStart + 10 >= flowNames?.length - ? "hidden" - : "visible", - }} - > - - + = flowNames?.length + } + sx={{ + ml: 1, + visibility: + currentPageStart + 10 >= + flowNames?.length + ? "hidden" + : "visible", + }} + > + + + + - - - )} - - )} + )} + + )} {nextLessonAndHome && ( { )} - - - {(props.pageName === "wordsorimage" || - props.pageName === "m5") && - storedData?.map((elem, index) => ( - 0 - ? { xs: "10px", md: "25px" } - : 0 - } + {(props.pageName === "wordsorimage" || + props.pageName === "m5") && + storedData?.length > 0 && ( + + + - - {elem?.audioUrl ? ( - + ) : ( + + )} + + + + {/* Status Icon */} + + {elem?.correctAnswer === false ? ( + wrongImage + ) : ( + correctImage + )} + - {elem?.correctAnswer === false ? ( - wrongImage - ) : ( - correctImage - )} + {/* Word Text */} + + {elem.selectedAnswer || "—"} + + + ))} + + + {(fluency || + [10, 11, 12, 13, 14, 15].includes( + LEVEL + )) && ( + + turtleImage - {elem.selectedAnswer || "Binocular"} + {"Oops, a bit slow!"} - ))} - - {(fluency || - [10, 11, 12, 13, 14, 15].includes(LEVEL)) && ( - - turtleImage - - {"Oops, a bit slow!"} - - + )} + )} - {/* second stack below*/} { width: "100%", }} > + {/* Progress bar removed from second Card - using the one in first Card instead */} - { - { + if ( + (LEVEL === 1 || LEVEL === 2) && + (mFlow === true || mFlow === "true") + ) { + //console.log("mFlow value:", mFlow); + // setLocalData("rFlow", true); + setLocalData("rStepZero", 0); + } + // if ( + // LEVEL === 1 || + // LEVEL === 2 || + // LEVEL === 3 || + // LEVEL === 4 || + // LEVEL === 6 || + // LEVEL === 9 + // ) { + // setLocalData("tFlow", true); + // navigate("/_practice"); + // } + if ( + props.pageName === "wordsorimage" || + props.pageName === "m5" + ) { + resetStoredData(); + } + if (isShowCase && !startShowCase && !gameOverData) { + setStartShowCase(true); + // 🎬 Trigger alphabet demo ONLY for F1 flow deferred milestones (A-step indices) + // Derive from F1_FLOW so it stays in sync with Practice.jsx + if (isF1FlowActive) { + const deferredMilestones = F1_FLOW.reduce( + (acc, step, idx) => { + if (step.type === "A") acc.push(idx); + return acc; + }, + [] + ); + const currentF1Index = Number( + getLocalData("f1FlowIndex") || -1 + ); + if (deferredMilestones.includes(currentF1Index)) { + window.dispatchEvent( + new Event("alphabetDemoTriggerRequest") + ); + } + } + } + if (gameOverData) { + gameOverData.link + ? navigate(gameOverData.link) + : navigate("/_practice"); + } + }} + > + - {showProgress && ( - - - {" "} - - {practiceSteps.map((elem, i) => { - return ( - i - ? "linear-gradient(90deg, rgba(132, 246, 48, 0.1) 0%, rgba(64, 149, 0, 0.1) 95%)" - : currentPracticeStep === i - ? "linear-gradient(90deg, #FF4BC2 0%, #C20281 95%)" - : "rgba(0, 0, 0, 0.04)", - ml: { - md: 1.5, - lg: 2, - }, - mr: - i === practiceSteps?.length - 1 - ? 2 - : 0, - borderRadius: "30px", - display: "flex", - justifyContent: "center", - alignItems: "center", - }} - > - {currentPracticeStep > i ? ( - - ) : ( - - {language !== "en" - ? elem.name - : LEVEL === 1 - ? elem.title - : elem.name} - - )} - - ); - })} - - - - )} - - { - if ( - (LEVEL === 1 || LEVEL === 2) && - (mFlow === true || mFlow === "true") - ) { - //console.log("mFlow value:", mFlow); - setLocalData("rFlow", true); - setLocalData("rStepZero", 0); - } - // if ( - // LEVEL === 1 || - // LEVEL === 2 || - // LEVEL === 3 || - // LEVEL === 4 || - // LEVEL === 6 || - // LEVEL === 9 - // ) { - // setLocalData("tFlow", true); - // navigate("/_practice"); - // } - if ( - props.pageName === "wordsorimage" || - props.pageName === "m5" - ) { - resetStoredData(); - } - if ( - isShowCase && - !startShowCase && - !gameOverData - ) { - setStartShowCase(true); - } - if (gameOverData) { - gameOverData.link - ? navigate(gameOverData.link) - : navigate("/_practice"); - } - }} - > - - {!gameOverData ? "Start Game ➜" : "Practice ➜"} - - - - - } + {!gameOverData ? "Start Game ➜" : "Practice ➜"} + + @@ -1581,121 +2184,7 @@ const MainLayout = (props) => { width={"100%"} height={"100%"} /> - - - - { - - {showProgress && ( - - - - {practiceSteps.map((elem, i) => { - return ( - - {/* {currentPracticeStep > i ? ( */} - - {/* ) : ( - - {LEVEL === 1 ? elem.title : elem.name} - - )} */} - - ); - })} - - - - )} - - } - - + {/* Removed duplicate progress bar section - using the one above instead */} )} diff --git a/src/components/Mechanism/WordsOrImage.jsx b/src/components/Mechanism/WordsOrImage.jsx index c54aec5d..532df070 100644 --- a/src/components/Mechanism/WordsOrImage.jsx +++ b/src/components/Mechanism/WordsOrImage.jsx @@ -22,6 +22,7 @@ import { getLocalData, setLocalData, } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import MainLayout from "../Layouts.jsx/MainLayout"; import PropTypes from "prop-types"; import { phoneticMatch } from "../../utils/phoneticUtils"; @@ -43,6 +44,7 @@ import { import { filterBadWords } from "@tekdi/multilingual-profanity-filter"; import { green } from "@mui/material/colors"; import AudioTooltipModal from "../Practice/AudioTooltipModal"; +import ZoomableImage from "../Practice/ZoomableImage"; // const isChrome = // /Chrome/.test(navigator.userAgent) && @@ -83,6 +85,7 @@ const WordsOrImage = ({ callUpdateLearner, disableScreen, isShowCase, + isDemo = false, handleBack, setEnableNext, startShowCase, @@ -100,21 +103,93 @@ const WordsOrImage = ({ vocabCount, wordCount, multilingual, + // Demo mode props + showSpeakButton: showSpeakButtonProp, + showStopButton: showStopButtonProp, + showListenRetryButtons: showListenRetryButtonsProp, + isRecording: isRecordingProp, + isAudioPlaying: isAudioPlayingProp, + onMicClick, + onStopClick, + onPlayClick, + onRetryClick, + onNextClick, + isInstructionPlaying, + onInteractionComplete, + showPointer = false, + pointerTarget = "", }) => { const audioRefs = createRef(null); const [audioInstance, setAudioInstance] = useState(null); const [isReady, setIsReady] = useState(false); - const [isPlaying, setIsPlaying] = useState(false); + const [isPlayingState, setIsPlaying] = useState(false); const [storedData, setStoredData] = useState([]); const [recognition, setRecognition] = useState(null); - const [isRecording, setIsRecording] = useState(false); - const [showSpeakButton, setShowSpeakButton] = useState(true); - const [showStopButton, setShowStopButton] = useState(false); - const [showListenRetryButtons, setShowListenRetryButtons] = useState(false); + const [isRecordingState, setIsRecording] = useState(false); + const [showSpeakButtonState, setShowSpeakButton] = useState(true); + const [showStopButtonState, setShowStopButton] = useState(false); + const [showListenRetryButtonsState, setShowListenRetryButtons] = + useState(false); const [answer, setAnswer] = useState(null); + + // Use prop values if in demo mode, otherwise use internal state + const isRecording = + isDemo && isRecordingProp !== undefined + ? isRecordingProp + : isRecordingState; + const showSpeakButton = + isDemo && showSpeakButtonProp !== undefined + ? showSpeakButtonProp + : showSpeakButtonState; + const showStopButton = + isDemo && showStopButtonProp !== undefined + ? showStopButtonProp + : showStopButtonState; + const showListenRetryButtons = + isDemo && showListenRetryButtonsProp !== undefined + ? showListenRetryButtonsProp + : showListenRetryButtonsState; + const isPlaying = + isDemo && isAudioPlayingProp !== undefined + ? isAudioPlayingProp + : isPlayingState; const [recordedAudioBlob, setRecordedAudioBlob] = useState(null); const [showHint, setShowHint] = useState(false); const [imageLoaded, setImageLoaded] = useState(false); + + // Get native language symbol based on nativeLang + const getNativeLangSymbol = () => { + const nativeLang = getLocalData("nativeLang"); + const langSymbolMap = { + ka: "ಕ", // Kannada (from LanguageModal) + kn: "ಕ", // Kannada (from AllLanguages) + tn: "இ", // Tamil (from AllLanguages, using "ta" symbol) + ta: "இ", // Tamil (from AllLanguages) + te: "ఈ", // Telugu (from AllLanguages) + hi: "क", // Hindi (from AllLanguages) + gu: "ક", // Gujarati (from AllLanguages) + or: "କ", // Odia (from AllLanguages) + }; + return langSymbolMap[nativeLang] || "ಕ"; // Default to Kannada if not found + }; + const nativeLangSymbol = getNativeLangSymbol(); + + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); const [abusiveFound, setAbusiveFound] = useState(false); @@ -237,7 +312,6 @@ const WordsOrImage = ({ mediaRecorder.onstop = () => { if (recordedChunksRef.current.length === 0) { - console.warn("No audio data captured."); setRecordedBlob(null); return; } @@ -316,8 +390,8 @@ const WordsOrImage = ({ audioRefNew.current.pause(); audioRefNew.current.currentTime = 0; audioRefNew.current = null; - setIsPlaying(false); } + setIsPlaying(false); }, []); const initializeRecognition = () => { @@ -347,12 +421,21 @@ const WordsOrImage = ({ setShowStopButton(false); setShowListenRetryButtons(true); + // Check if transcript is empty - if user didn't speak, mark as incorrect + if (!transcript || transcript.trim().length === 0) { + setAnswer(false); + const audio = new Audio(wrongSound); + audio.play(); + stopAudioRecording(); + return; + } + const matchPercentage = phoneticMatch( currentWordRef.current, transcript ); - if (matchPercentage < 49) { + if (matchPercentage < 80) { setAnswer(false); const audio = new Audio(wrongSound); audio.play(); @@ -368,7 +451,7 @@ const WordsOrImage = ({ setIsRecording(false); console.error("Speech recognition error:", event.error); if (event.error === "no-speech") { - console.log("No Speech!"); + // No speech detected } else if (event.error === "aborted") { recognitionInstance.start(); } @@ -407,7 +490,17 @@ const WordsOrImage = ({ SpeechRecognition.stopListening(); stopAudioRecording(); const finalTranscript = transcriptRef.current; - //console.log("transcript", finalTranscript, currentWordRef.current); + + // Check if transcript is empty - if user didn't speak, mark as incorrect + if (!finalTranscript || finalTranscript.trim().length === 0) { + setAnswer(false); + const audio = new Audio(wrongSound); + audio.play(); + setIsRecording(false); + setShowStopButton(false); + setShowListenRetryButtons(true); + return; + } const matchPercentage = phoneticMatch( currentWordRef.current, @@ -423,7 +516,7 @@ const WordsOrImage = ({ const audio = new Audio(correctSound); audio.play(); } else { - if (matchPercentage < 49) { + if (matchPercentage < 80) { setAnswer(false); const audio = new Audio(wrongSound); audio.play(); @@ -578,15 +671,11 @@ const WordsOrImage = ({ // return "green"; // } - console.log("ccc", answer); - if (answer === true) return "green"; if (answer === false) return "red"; return "#333F61"; }; - console.log("isTranscriptCorrect", isTranscriptCorrect); - //console.log("wds", words, matchedChar, answer); return ( @@ -628,11 +717,22 @@ const WordsOrImage = ({ component="h4" sx={{ color: "#333F61", - fontSize: isMobile ? "20px" : isTablet ? "25px" : "30px", + fontSize: + language === "te" + ? isMobile + ? "26px" + : isTablet + ? "31px" + : "36px" + : isMobile + ? "20px" + : isTablet + ? "25px" + : "30px", letterSpacing: "1.5px", lineHeight: "normal", fontWeight: 600, - fontFamily: "Quicksand", + fontFamily: getFontFamily(language), marginLeft: isMobile ? "0px" : "20px", textAlign: "center", }} @@ -642,7 +742,7 @@ const WordsOrImage = ({ @@ -761,34 +861,34 @@ const WordsOrImage = ({ justifyContent: "center", width: "100%", mb: isMobile ? 2 : 0, + position: "relative", }} > - setImageLoaded(true)} // When image loads, set state to true - onError={(e) => { - e.target.style.display = "none"; // Hide if error occurs - setImageLoaded(false); - }} - style={{ - width: "100%", // Image will take full width of the parent container + alt="Responsive content" + imageStyle={{ + width: "100%", maxWidth: isMobile ? "150px" : isTablet ? "350px" : "400px", marginBottom: isMobile ? "10px" : "40px", - height: "auto", // Maintain aspect ratio + height: "auto", maxHeight: isMobile ? "200px" : isTablet ? "280px" : "340px", - objectFit: "contain", // Ensures the image fits well within the dimensions + objectFit: "contain", marginRight: isMobile ? "30px" : "0px", }} - alt="Responsive content" // Adding alt text for accessibility + containerStyle={{ + display: "flex", + justifyContent: "center", + }} /> {hints && ( {words} - {isTranscriptCorrect !== null && ( + {/* Show multilingual box only for English */} + {isTranscriptCorrect !== null && language === "en" && ( - ಕ + {nativeLangSymbol} @@ -1191,14 +1308,43 @@ const WordsOrImage = ({ mt: isMobile ? 2 : 0, }} > - {level === 15 && !isShowCase ? ( + {(level === 15 && !isShowCase) || isDemo ? (
{showSpeakButton && ( startRecording(words, true)} + sx={{ + position: "relative", + cursor: + isDemo && isInstructionPlaying + ? "not-allowed" + : "pointer", + opacity: isDemo && isInstructionPlaying ? 0.5 : 1, + pointerEvents: + isDemo && isInstructionPlaying ? "none" : "auto", + }} + onClick={() => + isDemo && onMicClick + ? onMicClick() + : startRecording(words, true) + } > + {showPointer && pointerTarget === "mic" && ( + + 👆 + + )} )} {showStopButton && ( @@ -1212,10 +1358,39 @@ const WordsOrImage = ({ }} > stopRecording(words)} + sx={{ + position: "relative", + cursor: + isDemo && isInstructionPlaying + ? "not-allowed" + : "pointer", + opacity: isDemo && isInstructionPlaying ? 0.5 : 1, + pointerEvents: + isDemo && isInstructionPlaying ? "none" : "auto", + }} + onClick={() => + isDemo && onStopClick + ? onStopClick() + : stopRecording(words) + } > + {showPointer && pointerTarget === "stop" && ( + + 👆 + + )} - + {isRecording && }
)} @@ -1270,7 +1445,13 @@ const WordsOrImage = ({
) : ( -
+
{ - playRecordings(); + if (isDemo && onPlayClick) { + onPlayClick(); + } else { + playRecordings(); + } //setIsPlaying(true); }} //disabled={!recordedAudioBlob} @@ -1295,24 +1487,101 @@ const WordsOrImage = ({ style={{ height: "70px", width: "70px", - cursor: "pointer", + cursor: + isDemo && isInstructionPlaying + ? "not-allowed" + : "pointer", }} /> - {/* */} + {showPointer && pointerTarget === "play" && ( + + 👆 + + )}
)} retryRecording(words, true)} + sx={{ + position: "relative", + cursor: + isDemo && isInstructionPlaying + ? "not-allowed" + : "pointer", + marginLeft: "16px", + opacity: isDemo && isInstructionPlaying ? 0.5 : 1, + pointerEvents: + isDemo && isInstructionPlaying ? "none" : "auto", + }} + onClick={() => + isDemo && onRetryClick + ? onRetryClick() + : retryRecording(words, true) + } > + {showPointer && pointerTarget === "retry" && ( + + 👆 + + )} nextRecording()} + sx={{ + position: "relative", + cursor: + isDemo && isInstructionPlaying + ? "not-allowed" + : "pointer", + marginLeft: "16px", + opacity: isDemo && isInstructionPlaying ? 0.5 : 1, + pointerEvents: + isDemo && isInstructionPlaying ? "none" : "auto", + }} + onClick={() => + isDemo && onNextClick ? onNextClick() : nextRecording() + } > + {showPointer && pointerTarget === "continue" && ( + + 👆 + + )}
)} @@ -1348,11 +1617,20 @@ const WordsOrImage = ({ setOpenMessageDialog, isNextButtonCalled, setIsNextButtonCalled, + onInteractionComplete, }} /> )} + {isDemo && ( + + )} ); }; diff --git a/src/components/MilestoneForm/MilestoneFormDialog.jsx b/src/components/MilestoneForm/MilestoneFormDialog.jsx new file mode 100644 index 00000000..5b334484 --- /dev/null +++ b/src/components/MilestoneForm/MilestoneFormDialog.jsx @@ -0,0 +1,263 @@ +import React, { useState, useEffect } from "react"; +import { + Box, + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Typography, +} from "@mui/material"; +import { useLocation } from "react-router-dom"; +import { getLocalData } from "../../utils/constants"; +import { setMilestoneScore } from "../../services/learnerAi/learnerAiService"; + +const MilestoneFormDialog = ({ language, onSuccess, onError }) => { + const location = useLocation(); + const [openMilestoneForm, setOpenMilestoneForm] = useState(false); + const [formData, setFormData] = useState({ + language: language || "en", + milestone_level: "", + session_id: "", + sub_session_id: "", + }); + const [isSubmitting, setIsSubmitting] = useState(false); + const [message, setMessage] = useState({ text: "", isError: false }); + + // Auto-open form when on /Reset route + useEffect(() => { + if (location.pathname === "/Reset" && !openMilestoneForm) { + const sessionId = getLocalData("sessionId") || ""; + const subSessionId = getLocalData("sub_session_id") || ""; + setFormData({ + language: language || "en", + milestone_level: "", + session_id: sessionId, + sub_session_id: subSessionId, + }); + setOpenMilestoneForm(true); + } + }, [location.pathname, language, openMilestoneForm]); + + // Close dialog if route changes away from /Reset + useEffect(() => { + if (location.pathname !== "/Reset" && openMilestoneForm) { + setOpenMilestoneForm(false); + } + }, [location.pathname, openMilestoneForm]); + + const handleSubmit = async () => { + if ( + !formData.language || + !formData.milestone_level || + !formData.session_id || + !formData.sub_session_id + ) { + setMessage({ text: "Please fill all fields", isError: true }); + return; + } + + setIsSubmitting(true); + setMessage({ text: "", isError: false }); + try { + await setMilestoneScore( + formData.language, + formData.milestone_level, + formData.session_id, + formData.sub_session_id + ); + setMessage({ text: "Milestone score set successfully!", isError: false }); + if (onSuccess) { + onSuccess("Milestone score set successfully!"); + } + // Reset form after success + setTimeout(() => { + setOpenMilestoneForm(false); + setFormData({ + language: language || "en", + milestone_level: "", + session_id: "", + sub_session_id: "", + }); + setMessage({ text: "", isError: false }); + }, 2000); + } catch (error) { + setMessage({ + text: "Failed to set milestone. Please try again.", + isError: true, + }); + if (onError) { + onError("Failed to set milestone. Please try again."); + } + } finally { + setIsSubmitting(false); + } + }; + + // Only render if on /Reset route + if (location.pathname !== "/Reset") { + return null; + } + + return ( + !isSubmitting && setOpenMilestoneForm(false)} + maxWidth="sm" + fullWidth + PaperProps={{ + sx: { + borderRadius: "20px", + padding: "20px", + }, + }} + > + + Set Milestone + + + + + setFormData({ ...formData, language: e.target.value }) + } + fullWidth + disabled={isSubmitting} + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: "10px", + }, + }} + /> + + setFormData({ ...formData, milestone_level: e.target.value }) + } + fullWidth + disabled={isSubmitting} + placeholder="e.g., m5" + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: "10px", + }, + }} + /> + + setFormData({ ...formData, session_id: e.target.value }) + } + fullWidth + disabled={isSubmitting} + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: "10px", + }, + }} + /> + + setFormData({ ...formData, sub_session_id: e.target.value }) + } + fullWidth + disabled={isSubmitting} + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: "10px", + }, + }} + /> + {/* Success/Error Message */} + {message.text && ( + + + {message.text} + + + )} + + + + + + + + ); +}; + +export default MilestoneFormDialog; diff --git a/src/components/MilestoneForm/index.js b/src/components/MilestoneForm/index.js new file mode 100644 index 00000000..e70a058b --- /dev/null +++ b/src/components/MilestoneForm/index.js @@ -0,0 +1 @@ +export { default as MilestoneFormDialog } from "./MilestoneFormDialog"; diff --git a/src/components/Practice/AnouncementFlow.jsx b/src/components/Practice/AnouncementFlow.jsx index eb4c5e61..a5d813a2 100644 --- a/src/components/Practice/AnouncementFlow.jsx +++ b/src/components/Practice/AnouncementFlow.jsx @@ -900,7 +900,13 @@ const AnouncementFlow = ({ height={isMobile ? "" : "130px"} width={isMobile ? "100px" : ""} //onClick={handleNextClick} - style={{ cursor: "pointer", marginTop: "0px", zIndex: "9999" }} + style={{ + cursor: "pointer", + marginTop: "0px", + zIndex: "9999", + height: isMobile ? "auto" : "130px", + width: isMobile ? "100px" : "auto", + }} />
)} diff --git a/src/components/Practice/AserFlow.jsx b/src/components/Practice/AserFlow.jsx index c9c136ad..abd0ab9e 100644 --- a/src/components/Practice/AserFlow.jsx +++ b/src/components/Practice/AserFlow.jsx @@ -1,4 +1,5 @@ import React, { useState, useEffect, useRef } from "react"; +import { createPortal } from "react-dom"; import Confetti from "react-confetti"; import headerImg from "../../assets/headerImg.svg"; import speakButton from "../../assets/speakButton.svg"; @@ -40,7 +41,6 @@ import { callTelemetryDiscovery, } from "../../utils/apiUtil"; import AudioTooltipModal from "./AudioTooltipModal"; -import { loadTranscriber } from "../../utils/transcriber"; import { doubleMetaphone } from "double-metaphone"; import correctSound from "../../assets/correct.wav"; import wrongSound from "../../assets/audio/wrong.wav"; @@ -50,7 +50,11 @@ import { fetchUserPoints, createLearnerProgress, } from "../../services/orchestration/orchestrationService"; -import { fetchGetSetResult } from "../../services/learnerAi/learnerAiService"; +import { + fetchGetSetResult, + callEngagementPredictor, + clearInteractions, +} from "../../services/learnerAi/learnerAiService"; import { fetchAssessmentData, fetchPaginatedContent, @@ -63,6 +67,7 @@ import magnifier from "../../assets/magnifier.png"; import { Box } from "@mui/material"; import listenBearGif from "../../assets/beardances.gif"; import hintimg from "../../assets/hintsicon.svg"; +import { MessageDialog } from "../Assesment/Assesment"; const AserFlow = ({ // setVoiceText, @@ -87,17 +92,25 @@ const AserFlow = ({ playTeacherAudio = () => {}, callUpdateLearner, // disableScreen, - isShowCase, + isDemo, handleBack, // setEnableNext, loading, - // setOpenMessageDialog, + setOpenMessageDialog, audio, currentImg, vocabCount, wordCount, multilingual, contentSourceData, + // Demo mode props + onSpeakerClick, + onBubbleClick, + disableSpeaker = false, + disableBubbles = false, + hideContentDuringDemo = false, + blockProgression = false, + hideProgress = false, }) => { const [currentIndex, setCurrentIndex] = useState(0); const [selectedLetter, setSelectedLetter] = useState(""); @@ -123,21 +136,27 @@ const AserFlow = ({ const [initialAssesment, setInitialAssesment] = useState(true); const [disableScreen, setDisableScreen] = useState(false); // const [play] = useSound(LevelCompleteAudio); - const [openMessageDialog, setOpenMessageDialog] = useState(""); const [totalSyllableCount, setTotalSyllableCount] = useState(""); + // Track step start time for duration calculation + const [stepStartTime] = useState(Date.now()); const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); + const isCompletionCalledRef = useRef(false); const [questions, setQuestions] = useState([]); - const [ansSelectionStatus, setAnsSelectionStatus] = useState({}); + // Track character selections for ansSelectionStatus - now an array of objects + const [ansSelectionStatus, setAnsSelectionStatus] = useState([]); + const ansSelectionStatusRef = useRef([]); const lang = getLocalData("lang"); const virtualId = getLocalData("virtualId"); const [clickedIndex, setClickedIndex] = useState(null); const [isAudioPlaying, setIsAudioPlaying] = useState(false); const [open, setOpen] = useState(false); const [currentItemNumber, setCurrentItemNumber] = useState(0); - const TOTAL_ITEMS = 12; + const [showSuccessMessage, setShowSuccessMessage] = useState(false); + // In demo mode, only show 1 question; otherwise show 10 + const TOTAL_ITEMS = isDemo ? 1 : 10; const completionPercentage = Math.min( - (currentItemNumber / TOTAL_ITEMS) * 100, + (Math.min(currentItemNumber + 1, TOTAL_ITEMS) / TOTAL_ITEMS) * 100, 100 ); @@ -160,19 +179,24 @@ const AserFlow = ({ return; } + // In demo mode, only fetch 1 question; otherwise fetch 10 + const questionCount = isDemo ? 1 : 10; const resPagination = await fetchPaginatedContent( sentences.collectionId, - 10 + questionCount ); - await addLesson({ - sessionId, - milestone: `practice`, - lesson: "0", - progress: 0, - language: lang, - milestoneLevel: "B", - }); + // Only call addLesson if not in preview/demo mode + if (!isDemo) { + await addLesson({ + sessionId, + milestone: `practice`, + lesson: "0", + progress: 0, + language: lang, + milestoneLevel: "B", + }); + } // Update state setCurrentContentType("Char"); @@ -201,9 +225,12 @@ const AserFlow = ({ (ch) => !existingLetters.includes(ch) ); + // In demo mode, add more distractors (7-8) to have enough bubbles for selection + // In normal mode, add 2 distractors + const distractorCount = isDemo ? 7 : 2; const extraChars = availableChars .sort(() => 0.5 - Math.random()) - .slice(0, 2); + .slice(0, distractorCount); const extraQuestions = extraChars.map((ch) => ({ contentId: `fake_${ch}`, @@ -218,11 +245,18 @@ const AserFlow = ({ console.error("Error fetching data:", error); } })(); - }, []); + }, [isDemo, sessionId]); useEffect(() => { if (questions?.length) { - setLocalData("sub_session_id", uniqueId()); + const oldSubSessionId = getLocalData("sub_session_id"); + const newSubSessionId = uniqueId(); + setLocalData("sub_session_id", newSubSessionId); + + // Clear interactions for old sub session if it exists + if (oldSubSessionId) { + clearInteractions(oldSubSessionId); + } } }, [questions]); @@ -231,15 +265,47 @@ const AserFlow = ({ const questionLetters = questions.map((q) => q.contentSourceData[0].text); + const stopCurrentAudio = () => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + audioRef.current = null; + } + setIsAudioPlaying(false); + }; + const handlePlayAudio = () => { - const audio = new Audio( - `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${currentItem?.contentId}.wav` - ); - setIsAudioPlaying(true); - audio.play(); + // If in demo mode and custom handler provided, call it (in addition to normal flow) + if (isDemo && onSpeakerClick) { + onSpeakerClick(); + } + + // Only play audio if contentId exists and is not a fake item + if (currentItem?.contentId && !currentItem?.isFake) { + try { + stopCurrentAudio(); + const audio = new Audio( + `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${currentItem.contentId}.wav` + ); + audioRef.current = audio; + setIsAudioPlaying(true); + + audio.onended = () => setIsAudioPlaying(false); + audio.onerror = () => setIsAudioPlaying(false); + audio.play().catch(() => setIsAudioPlaying(false)); + } catch (error) { + console.error("Error playing audio:", error); + setIsAudioPlaying(false); + } + } }; const handleCompletion = async () => { + // Skip API calls in preview/demo mode + if (isDemo) { + return; + } + const sub_session_id = getLocalData("sub_session_id"); try { @@ -257,11 +323,10 @@ const AserFlow = ({ contentType: "Char", mechanics_id: getLocalData("mechanism_id") || "", milestone: milestoneLevel, - ansSelectionStatus: ansSelectionStatus, + ansSelectionStatus: ansSelectionStatusRef.current, }; const result = await updateLearnerProfile(lang, requestBody); - console.log("Learner progress result:", result); } catch (error) { console.error("Error creating learner progress:", error); } @@ -273,7 +338,23 @@ const AserFlow = ({ currentCollectionId, totalSyllableCount ); - console.log("GetSet result:", getSetResultRes); + const { data } = getSetResultRes; + + // Call engagement predictor after getsetresult + // Interactions and lesson are automatically retrieved + callEngagementPredictor(sub_session_id); + + await addLesson({ + sessionId, + milestone: `practice`, + lesson: data?.currentLevel !== "B" ? "0" : "1", + progress: data?.currentLevel !== "B" ? 0 : 5, + language: lang, + milestoneLevel: data?.currentLevel || "B", + ...(data?.currentLevel === "B" && { subMilestoneLevel: "F1" }), + duration: Math.round((Date.now() - stepStartTime) / 1000), + applyLevel: data?.currentLevel === "B" ? "L1" : undefined, + }); } catch (error) { console.error("Error fetching set result:", error); } @@ -315,59 +396,86 @@ const AserFlow = ({ }; const handleBubbleClick = (letter, index) => { + if (isCompletionCalledRef.current) return; + stopCurrentAudio(); setClickedIndex(index); - setIsAudioPlaying(false); - setTimeout(() => setClickedIndex(null), 1000); setSelectedLetter(letter); const correct = letter === correctLetter; setIsCorrect(correct); setShowNext(true); - setAnsSelectionStatus((prev) => ({ - ...prev, - [letter]: correct, - })); + const newEntry = { + text: letter, + status: correct, + gameType: "letter-hunt", + }; + ansSelectionStatusRef.current = [ + ...ansSelectionStatusRef.current, + newEntry, + ]; + setAnsSelectionStatus(ansSelectionStatusRef.current); if (correct) correctAudio.play(); else wrongAudio.play(); - // ✅ Increase progress on every bubble click - setCurrentItemNumber((prev) => Math.min(prev + 1, TOTAL_ITEMS)); + if (isDemo && onBubbleClick) { + onBubbleClick(letter, index, correct); + if (correct) return; + } - setTimeout(() => { - handleNextClick(); - }, 1000); + handleNextClick(correct); }; - const handleNextClick = async () => { + const handleNextClick = async (wasCorrect = false) => { + // In demo mode, don't proceed to next question - preview component handles completion + if (isDemo) { + return; + } + + // If all items are completed, handle navigation + if (currentItemNumber >= TOTAL_ITEMS) { + if (isCompletionCalledRef.current) return; + return; + } + setSelectedLetter(""); setIsCorrect(null); setShowNext(false); - setIsAudioPlaying(false); - if (currentIndex < questions.length - 3) { - setCurrentIndex((prev) => prev + 1); - } else { - const totalAnswered = Object.keys(ansSelectionStatus).length; - const correctCount = Object.values(ansSelectionStatus).filter( - (val) => val === true - ).length; + // Increment progress for both correct and wrong answers + const newItemNumber = Math.min(currentItemNumber + 1, TOTAL_ITEMS); + setCurrentItemNumber(newItemNumber); + + console.log("AserFlow - Progress update:", { + currentItemNumber, + newItemNumber, + TOTAL_ITEMS, + isComplete: newItemNumber >= TOTAL_ITEMS, + }); + + // After completing 10 items (regardless of correct/wrong) + if (newItemNumber >= TOTAL_ITEMS) { + if (isCompletionCalledRef.current) return; + isCompletionCalledRef.current = true; + console.log( + "AserFlow - All 10 items completed! Next button should appear." + ); + await handleCompletion(); + setLocalData("rFlow", false); + // Skip telemetry in preview/demo mode + if (!isDemo) { + callTelemetryDiscovery("Discovery-AserFlow"); + } - const correctPercentage = - totalAnswered > 0 ? (correctCount / totalAnswered) * 100 : 0; + setShowSuccessMessage(true); - if (correctPercentage >= 80) { - await handleCompletion(); - setLocalData("rFlow", false); - } - callTelemetryDiscovery("Discovery-AserFlow"); - handleNext?.(); - if (process.env.REACT_APP_IS_APP_IFRAME === "true") { - navigate("/"); - } else { - navigate("/discover-start"); - } + return; + } + + // Always move to next question (whether correct or wrong) + if (currentIndex < questions.length - 1) { + setCurrentIndex((prev) => prev + 1); } }; @@ -416,11 +524,14 @@ const AserFlow = ({ position: "absolute", top: 10, right: 20, - display: "flex", + display: hideProgress ? "none" : "flex", flexDirection: "column", alignItems: "center", width: "120px", zIndex: 2000, // keeps above everything + opacity: hideContentDuringDemo ? 0 : 1, + visibility: hideContentDuringDemo ? "hidden" : "visible", + transition: "opacity 0.3s ease", }} > - {currentItemNumber}/{TOTAL_ITEMS} + {Math.min(currentItemNumber + 1, TOTAL_ITEMS)}/{TOTAL_ITEMS} setOpen(true)} /> @@ -553,6 +668,9 @@ const AserFlow = ({ borderRadius: "20px", //boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", overflow: "hidden", + opacity: hideContentDuringDemo ? 0 : 1, + visibility: hideContentDuringDemo ? "hidden" : "visible", + transition: "opacity 0.3s ease", }} > {questionLetters?.map((char, index) => { @@ -576,15 +694,21 @@ const AserFlow = ({ return (
handleBubbleClick(char, index)} + onClick={() => { + if (!disableBubbles) { + handleBubbleClick(char, index); + } + }} style={{ position: "absolute", top: pos.top, left: pos.left, transform: "translate(-50%, -50%)", - cursor: "pointer", + cursor: disableBubbles ? "not-allowed" : "pointer", textAlign: "center", zIndex: 99999, + opacity: disableBubbles ? 0.5 : 1, + pointerEvents: disableBubbles ? "none" : "auto", }} > {/* Bubble image */} @@ -601,12 +725,16 @@ const AserFlow = ({ style={{ width: "100px", height: "100px", - filter: - ansSelectionStatus[char] === true - ? "drop-shadow(0 0 10px #08a169ff)" - : ansSelectionStatus[char] === false - ? "drop-shadow(0 0 10px #d31818ff)" - : "drop-shadow(0 3px 8px rgba(0,0,0,0.3))", + filter: ansSelectionStatus.some( + (item) => item.text === char && item.status === true + ) + ? "drop-shadow(0 0 10px #08a169ff)" + : ansSelectionStatus.some( + (item) => + item.text === char && item.status === false + ) + ? "drop-shadow(0 0 10px #d31818ff)" + : "drop-shadow(0 3px 8px rgba(0,0,0,0.3))", transition: "filter 0.3s ease", }} /> @@ -644,6 +772,26 @@ const AserFlow = ({ > {char} + + {/* Pointer under bubble for demo - show only for correct bubble in demo mode */} + {isDemo && char === correctLetter && !disableBubbles && ( +
+ 👇 +
+ )}
); @@ -656,6 +804,9 @@ const AserFlow = ({ justifyContent: "center", alignItems: "center", display: "flex", + opacity: hideContentDuringDemo ? 0 : 1, + visibility: hideContentDuringDemo ? "hidden" : "visible", + transition: "opacity 0.3s ease", }} > { - handlePlayAudio(); + if (!disableSpeaker) { + handlePlayAudio(); + } }} > - next handleNextClick()} - /> + {/* Show next button only after completing all items (hide in demo mode) */} + {(() => { + // Don't show next button in demo mode - preview component handles completion + if (isDemo) return false; + const shouldShowNext = currentItemNumber >= TOTAL_ITEMS; + console.log("AserFlow - Next button render check:", { + currentItemNumber, + TOTAL_ITEMS, + shouldShowNext, + showSuccessMessage, + }); + return shouldShowNext; + })() && ( + next { + console.log("AserFlow - Next button clicked after completion"); + // Handle navigation when next button is clicked after completion + handleNext?.(); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); + } else { + navigate("/discover-start"); + } + }} + /> + )} bear
+ + {/* Animation styles for demo pointer */} + {isDemo && ( + + )} + + {/* Success Message Dialog with Panda - Rendered via Portal to ensure proper centering */} + {/* Don't show success message in demo mode - preview component handles completion */} + {showSuccessMessage && + !isDemo && + createPortal( + { + setShowSuccessMessage(false); + // Don't auto-navigate - let user click next button instead + // handleNext?.(); + // if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + // navigate("/"); + // } else { + // navigate("/discover-start"); + // } + }} + />, + document.body + )} ); }; diff --git a/src/components/Practice/AserFlowPreview.jsx b/src/components/Practice/AserFlowPreview.jsx new file mode 100644 index 00000000..7b429918 --- /dev/null +++ b/src/components/Practice/AserFlowPreview.jsx @@ -0,0 +1,628 @@ +import { useState, useRef } from "react"; +import { + getLocalData, + RetryIcon, + NextButtonRound, +} from "../../utils/constants"; +import CountdownTimer from "../CountdownTimer/CountdownTimer"; +import { Sparkles } from "lucide-react"; +import { Progress } from "../../lib/axl-explorations/src/components/ui/progress"; +import AserFlow from "./AserFlow"; + +const demoInstructions = { + en: { + narration1: + "Welcome to Letter Hunt! Click the speaker button to hear the letter sound.", + narration2: "Listen carefully to the letter sound", + narration3: "Now click on the bubble with the correct letter", + narration4: "Great job! You found the letter!", + skipDemo: "Skip Demo", + startGame: "Start Game", + howToPlay: "How to Play", + }, + te: { + narration1: + "లెటర్ హంట్‌కు స్వాగతం! అక్షర ధ్వనిని వినడానికి స్పీకర్ బటన్‌పై క్లిక్ చేయండి.", + narration2: "అక్షర ధ్వనిని జాగ్రత్తగా వినండి", + narration3: "ఇప్పుడు సరైన అక్షరం ఉన్న బబుల్‌పై క్లిక్ చేయండి", + narration4: "బాగా చేసారు! మీరు అక్షరాన్ని కనుగొన్నారు!", + skipDemo: "డెమోను దాటవేయండి", + startGame: "ఆట ప్రారంభించండి", + howToPlay: "ఎలా ఆడాలి", + }, + kn: { + narration1: + "ಲೆಟರ್ ಹಂಟ್‌ಗೆ ಸ್ವಾಗತ! ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಬಟನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ.", + narration2: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಆಲಿಸಿ", + narration3: "ಈಗ ಸರಿಯಾದ ಅಕ್ಷರವಿರುವ ಬಬಲ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಅಕ್ಷರವನ್ನು ಕಂಡುಕೊಂಡಿದ್ದೀರಿ!", + skipDemo: "ಡೆಮೊವನ್ನು ಬಿಟ್ಟುಬಿಡಿ", + startGame: "ಆಟ ಪ್ರಾರಂಭಿಸಿ", + howToPlay: "ಹೇಗೆ ಆಡಬೇಕು", + }, + mr: { + narration1: + "लेटर हंटमध्ये स्वागत आहे! अक्षर आवाज ऐकण्यासाठी स्पीकर बटणावर क्लिक करा.", + narration2: "अक्षर आवाज काळजीपूर्वक ऐका", + narration3: "आता योग्य अक्षर असलेल्या बुडबुड्यावर क्लिक करा", + narration4: "छान केलं! तुम्हाला अक्षर सापडले!", + skipDemo: "डेमो वगळा", + startGame: "खेळ सुरू करा", + howToPlay: "कसे खेळायचे", + }, + hi: { + narration1: + "लेटर हंट में आपका स्वागत है! अक्षर की ध्वनि सुनने के लिए स्पीकर बटन पर क्लिक करें।", + narration2: "अक्षर की ध्वनि ध्यान से सुनें", + narration3: "अब सही अक्षर वाले बुलबुले पर क्लिक करें", + narration4: "बहुत बढ़िया! आपने अक्षर ढूंढ लिया!", + skipDemo: "डेमो छोड़ें", + startGame: "खेल शुरू करें", + howToPlay: "कैसे खेलें", + }, + gu: { + narration1: + "લેટર હંટમાં આપનું સ્વાગત છે! અક્ષર ધ્વનિ સાંભળવા માટે સ્પીકર બટન પર ક્લિક કરો.", + narration2: "અક્ષર ધ્વનિ ધ્યાનથી સાંભળો", + narration3: "હવે સાચા અક્ષર સાથેના બબલ પર ક્લિક કરો", + narration4: "ખૂબ સરસ! તમે અક્ષર શોધી કાઢ્યું!", + skipDemo: "ડેમો છોડો", + startGame: "રમત શરૂ કરો", + howToPlay: "કેવી રીતે રમવું", + }, + ta: { + narration1: + "லெட்டர் ஹன்ட் வரவேற்கிறது! எழுத்து ஒலியைக் கேட்க ஸ்பீக்கர் பொத்தானைக் கிளிக் செய்யவும்.", + narration2: "எழுத்து ஒலியை கவனமாகக் கேளுங்கள்", + narration3: "இப்போது சரியான எழுத்து உள்ள குமிழியைக் கிளிக் செய்யவும்", + narration4: "அருமை! நீங்கள் எழுத்தைக் கண்டுபிடித்தீர்கள்!", + skipDemo: "டெமோவைத் தவிர்க்கவும்", + startGame: "விளையாட்டைத் தொடங்கவும்", + howToPlay: "எப்படி விளையாடுவது", + }, + or: { + narration1: + "ଲେଟର ହଣ୍ଟକୁ ସ୍ୱାଗତ! ଅକ୍ଷର ଧ୍ବନି ଶୁଣିବା ପାଇଁ ସ୍ପିକର ବଟନ୍ କ୍ଲିକ କରନ୍ତୁ।", + narration2: "ଅକ୍ଷର ଧ୍ବନି ଧ୍ୟାନରେ ଶୁଣନ୍ତୁ", + narration3: "ବର୍ତ୍ତମାନ ସଠିକ୍ ଅକ୍ଷର ଥିବା ବବୁଲ୍ କ୍ଲିକ୍ କରନ୍ତୁ", + narration4: "ବହୁତ ଭଲ! ଆପଣ ଅକ୍ଷର ଖୋଜିଲେ!", + skipDemo: "ଡେମୋ ଛାଡନ୍ତୁ", + startGame: "ଖେଳ ଆରମ୍ଭ କରନ୍ତୁ", + howToPlay: "କିପରି ଖେଳିବେ", + }, +}; + +const AserFlowPreview = ({ onStartGame, onBack }) => { + const [demoPhase, setDemoPhase] = useState("countdown"); + const [demoStep, setDemoStep] = useState("waitForSpeaker"); // "waitForSpeaker", "speakerClicked", "waitForLetter", "bubbleClicked" + const [currentDemoStep, setCurrentDemoStep] = useState(1); // Track demo step progress (1, 2, or 3) + const [showPointer, setShowPointer] = useState(false); + const [pointerTarget, setPointerTarget] = useState(""); // "speaker" or "letter" + const [isInstructionPlaying, setIsInstructionPlaying] = useState(false); + const [hasClickedSpeaker, setHasClickedSpeaker] = useState(false); + const [hasClickedBubble, setHasClickedBubble] = useState(false); + const [blockGameProgression, setBlockGameProgression] = useState(true); // Block until correct answer + const [gameKey, setGameKey] = useState(0); // Key to force remount of AserFlow + const [disableSpeaker, setDisableSpeaker] = useState(true); // Disable speaker initially + const [disableBubbles, setDisableBubbles] = useState(true); // Disable bubbles initially + const instructionAudioRef = useRef(null); // Ref to store current instruction audio + + const language = getLocalData("lang") || "en"; + const instructions = demoInstructions[language] || demoInstructions.en; + const TOTAL_DEMO_STEPS = 3; + + const handleCountdownComplete = () => { + setDemoPhase("demo"); + setDemoStep("waitForSpeaker"); + setCurrentDemoStep(1); // Step 1: Listen to instruction + setTimeout(async () => { + await playInstruction(instructions.narration1, 1); + // After instruction, enable ONLY speaker (bubbles still disabled) + setDisableSpeaker(false); + setShowPointer(true); + setPointerTarget("speaker"); + }, 1000); + }; + + const handleSpeakerClick = async () => { + // Only show instructions/pointer on first speaker click + if (hasClickedSpeaker || disableSpeaker) return; + + setHasClickedSpeaker(true); + setShowPointer(false); + setDisableSpeaker(true); // Disable speaker again + setDemoStep("speakerClicked"); + setCurrentDemoStep(2); // Step 2: Listen to letter sound + + // Show instruction to click bubble (after natural game audio plays) + setTimeout(async () => { + await playInstruction(instructions.narration3, 3); + // After instruction, enable BOTH bubbles and speaker (so user can replay audio) + setDisableBubbles(false); + setDisableSpeaker(false); // Re-enable speaker so user can replay letter audio + setDemoStep("waitForLetter"); + // Don't show pointer for bubble selection + }, 2000); // Wait for letter sound to play + }; + + const handleBubbleClick = async (letter, index, isCorrect) => { + // If user already completed the demo or bubbles are disabled, don't do anything + if (hasClickedBubble || disableBubbles) return; + + // If the answer is WRONG, let them try again (no pointer) + if (!isCorrect) { + return; + } + + // Answer is CORRECT - unblock progression and proceed to completion + setBlockGameProgression(false); // Allow game to progress now + setHasClickedBubble(true); + setShowPointer(false); + setDisableBubbles(true); // Disable bubbles after correct click + setDemoStep("bubbleClicked"); + setCurrentDemoStep(3); // Step 3: Select correct letter + + // Show completion screen after normal game feedback + setTimeout(async () => { + await playInstruction(instructions.narration4, 4); + setTimeout(() => { + setDemoPhase("completion"); + }, 1000); + }, 1500); // Wait for correct sound + }; + + const playInstruction = (text, step) => { + // Stop any currently playing instruction audio + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + + setIsInstructionPlaying(true); + + return new Promise((resolve) => { + // Build S3 audio path: /audio/audio-preview/combined-letter-games/letter-hunt/{language}/narration{step}.wav + // const audioPath = `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL || ''}/audio/audio-preview/combined-letter-games/letter-hunt/${language}/narration${step}.wav`; + const audioPath = `/audio/audio-preview/letter-hunt/${language}/narration${step}.wav`; + const audio = new Audio(audioPath); + instructionAudioRef.current = audio; // Store audio reference + + audio.onended = () => { + setIsInstructionPlaying(false); + instructionAudioRef.current = null; + resolve(); + }; + + audio.onerror = () => { + console.error(`Audio file not found at ${audioPath}`); + setIsInstructionPlaying(false); + instructionAudioRef.current = null; + resolve(); + }; + + audio.play().catch((error) => { + console.error(`Audio playback failed: ${error}`); + setIsInstructionPlaying(false); + instructionAudioRef.current = null; + resolve(); + }); + }); + }; + + const handleStartGameClick = () => { + // Stop any playing instruction audio + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + setIsInstructionPlaying(false); + onStartGame(); + }; + + const handleReplayDemo = () => { + setDemoPhase("countdown"); + setDemoStep("waitForSpeaker"); + setCurrentDemoStep(1); // Reset to step 1 + setShowPointer(false); + setPointerTarget(""); + setHasClickedSpeaker(false); + setHasClickedBubble(false); + setBlockGameProgression(true); // Block progression again for replay + setDisableSpeaker(true); // Reset to disabled + setDisableBubbles(true); // Reset to disabled + setGameKey((prev) => prev + 1); // Force remount of AserFlow to reset all game state + }; + + const handleSkipDemo = () => { + // Stop any playing instruction audio + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + setIsInstructionPlaying(false); + onStartGame(); + }; + + const handleBack = () => { + // Stop any playing instruction audio + if (instructionAudioRef.current) { + instructionAudioRef.current.pause(); + instructionAudioRef.current.currentTime = 0; + instructionAudioRef.current = null; + } + setIsInstructionPlaying(false); + onBack(); + }; + + const completionMessages = { + en: { + title: "Demo Complete!", + message: "Great job! You're ready to start hunting letters.", + replayText: "Replay", + continueText: "Continue", + }, + te: { + title: "డెమో పూర్తయింది!", + message: "బాగా చేసారు! మీరు లెటర్ల వేటకు సిద్ధంగా ఉన్నారు.", + replayText: "మళ్లీ ప్లే చేయండి", + continueText: "కొనసాగించు", + }, + kn: { + title: "ಡೆಮೊ ಪೂರ್ಣಗೊಂಡಿದೆ!", + message: "ಅದ್ಭುತ! ನೀವು ಅಕ್ಷರಗಳನ್ನು ಹುಡುಕಲು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ.", + replayText: "ಮರುಪ್ಲೇ", + continueText: "ಮುಂದುವರಿಸಿ", + }, + mr: { + title: "डेमो पूर्ण झाला!", + message: "छान केलं! तुम्ही अक्षरांची शिकार सुरू करण्यास तयार आहात.", + replayText: "पुन्हा प्ले करा", + continueText: "पुढे चला", + }, + hi: { + title: "डेमो पूर्ण हुआ!", + message: "बहुत बढ़िया! आप अक्षरों की खोज शुरू करने के लिए तैयार हैं।", + replayText: "फिर से प्ले करें", + continueText: "जारी रखें", + }, + gu: { + title: "ડેમો પૂર્ણ થયું!", + message: "ખૂબ સરસ! તમે અક્ષરોની શોધ શરૂ કરવા તૈયાર છો.", + replayText: "ફરીથી પ્લે કરો", + continueText: "ચાલુ રાખો", + }, + ta: { + title: "டெமோ முடிந்தது!", + message: + "நன்றாக செய்தீர்கள்! நீங்கள் எழுத்துக்களை வேட்டையாட தயாராக உள்ளீர்கள்.", + replayText: "மீண்டும் இயக்கு", + continueText: "தொடர்க", + }, + or: { + title: "ଡେମୋ ସମ୍ପୂର୍ଣ୍ଣ!", + message: "ବହୁତ ଭଲ! ଆପଣ ଅକ୍ଷର ଶିକାର ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ।", + replayText: "ପୁନର୍ବାର ପ୍ଲେ କରନ୍ତୁ", + continueText: "ଜାରି ରଖନ୍ତୁ", + }, + }; + + const messages = completionMessages[language] || completionMessages.en; + + return ( +
+ {/* Actual AserFlow Component */} + + + {/* "How to Play" Progress Indicator */} + {demoPhase === "demo" && ( +
+
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+
+ )} + + {/* Countdown Timer Overlay - Inside the UI */} + {demoPhase === "countdown" && ( +
+ +
+ )} + + {/* Demo Control Buttons - Inside White Container */} + {demoPhase === "demo" && ( +
+ {/* Skip Demo Button */} + + + {/* Start Game Button */} + +
+ )} + + {/* Pointer - Only for speaker button */} + {showPointer && + demoPhase === "demo" && + !isInstructionPlaying && + pointerTarget === "speaker" && ( +
+ 👇 +
+ )} + + {/* Completion Screen */} + {demoPhase === "completion" && ( +
+
+
🎉
+

+ {messages.title} +

+

+ {messages.message} +

+
+
{ + if (!isInstructionPlaying) { + e.currentTarget.style.transform = "scale(1.1)"; + } + }} + onMouseLeave={(e) => { + if (!isInstructionPlaying) { + e.currentTarget.style.transform = "scale(1)"; + } + }} + > + + + {messages.replayText} + +
+
{ + if (!isInstructionPlaying) { + e.currentTarget.style.transform = "scale(1.1)"; + } + }} + onMouseLeave={(e) => { + if (!isInstructionPlaying) { + e.currentTarget.style.transform = "scale(1)"; + } + }} + > + + + {messages.continueText} + +
+
+
+
+ )} + + +
+ ); +}; + +export default AserFlowPreview; diff --git a/src/components/Practice/BingoCard.jsx b/src/components/Practice/BingoCard.jsx index cfca32f3..ffb27681 100644 --- a/src/components/Practice/BingoCard.jsx +++ b/src/components/Practice/BingoCard.jsx @@ -45,7 +45,6 @@ import { } from "../../utils/apiUtil"; import { filterBadWords } from "@tekdi/multilingual-profanity-filter"; import AudioTooltipModal from "./AudioTooltipModal"; -import { loadTranscriber } from "../../utils/transcriber"; import { doubleMetaphone } from "double-metaphone"; import loadingJson from "../../assets/loadingJson.json"; import Lottie from "lottie-react"; @@ -124,11 +123,6 @@ const BingoPage = React.memo( return transformed?.arrM?.[currentWordIndex]; }, [transformed, currentWordIndex]); - console.log( - "currentWord", - transformed?.imageAudioMap?.[currentWord]?.audio - ); - const updateStoredData = useCallback((audio, isCorrect) => {}, []); const handleRecordingComplete = useCallback((base64Data) => { @@ -173,8 +167,6 @@ const BingoPage = React.memo( setLocalIsRecordingComplete(false); setLocalRecAudio(null); setLocalShowConfetti(false); - } else { - console.log("All words completed!"); } }, [currentWordIndex, transformed]); @@ -325,7 +317,7 @@ const BingoPage = React.memo( style={{ position: "absolute", bottom: "-42px", - left: "40%", + left: "15%", transform: "translateX(-50%)", height: "200px", animation: "jump 1.3s ease-in-out infinite", @@ -342,8 +334,6 @@ const BingoPage = React.memo( ); const SuccessPage = React.memo(({ score, completedPairs, onNext }) => { - console.log("SuccessPage rendering"); - return (
{ top: "150px", left: "50%", transform: "translateX(-50%)", + zIndex: 10, }} >

{ position: "relative", width: "100px", height: "100px", - marginTop: "60px", + marginTop: "140px", marginBottom: "30px", + zIndex: 5, }} > { @@ -729,7 +750,6 @@ const BingoCard = ({ mediaRecorder.onstop = async () => { if (recordedChunksRef.current.length === 0) { - console.warn("No audio data captured."); setRecordedBlob(null); return; } @@ -740,16 +760,9 @@ const BingoCard = ({ try { setIsLoading(true); - const transcriber = await loadTranscriber(); - const audioUrl = URL.createObjectURL(blob); - const output = await transcriber(audioUrl, { - chunk_length_s: 20, - stride_length_s: 5, - task: "transcribe", - language: "en", - }); - - const transcripts = sanitize(output.text); + + // Use browser speech recognition transcript (already captured during recording) + const transcripts = sanitize(transcriptRef.current || ""); const isCorrect = transcripts.includes(word) || phoneticMatch(transcripts, word); @@ -1016,6 +1029,12 @@ const BingoCard = ({ if (isCorrectPair) { setMatchedPair({ fullWord: currentWord, parts: requiredParts }); + // Play success sound when correct pair is matched + const audio = new Audio(correctSound); + audio.play().catch((error) => { + console.error("Error playing success sound:", error); + }); + setTimeout(() => setShowConfetti(true), 500); setTimeout(() => { @@ -1040,6 +1059,12 @@ const BingoCard = ({ setCurrentHintPair(null); }, 3500); } else { + // Play wrong sound when incorrect pair is selected + const audio = new Audio(wrongSound); + audio.play().catch((error) => { + console.error("Error playing wrong sound:", error); + }); + setWrongPair(newSelected); setTimeout(() => { setWrongPair([]); @@ -1553,7 +1578,7 @@ const BingoCard = ({ style={{ position: "absolute", bottom: "-42px", - left: "40%", + left: "20%", transform: "translateX(-50%)", height: "200px", animation: "jump 1.3s ease-in-out infinite", diff --git a/src/components/Practice/FluencyP1.jsx b/src/components/Practice/FluencyP1.jsx index efe3a203..bc974e80 100644 --- a/src/components/Practice/FluencyP1.jsx +++ b/src/components/Practice/FluencyP1.jsx @@ -26,6 +26,7 @@ import { getLocalData, setLocalData, } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import { phoneticMatch } from "../../utils/phoneticUtils"; import SpeechRecognition, { useSpeechRecognition, @@ -39,7 +40,6 @@ import { callTelemetryApi, } from "../../utils/apiUtil"; import AudioTooltipModal from "./AudioTooltipModal"; -import { loadTranscriber } from "../../utils/transcriber"; import { doubleMetaphone } from "double-metaphone"; import correctSound from "../../assets/correct.wav"; import tortoiseImg from "../../assets/TurtleCircle.gif"; @@ -61,16 +61,17 @@ const UnderlinedSentence = ({ hints, showUnderlines, onWordHover, + lang, }) => { const words = sentence.split(" "); return (

= 4 && level <= 9) { + currentLevel = practiceSteps?.[currentPracticeStep]?.name; + apiLevel = `M${level}-${currentLevel}`; + } + useEffect(() => { handleStart(); }, [parentWords]); @@ -402,18 +408,37 @@ const FluencyP1 = ({ } }; + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + const sentencesData = [ { id: 1, - sentence: contentSourceData.text, - audio: contentSourceData.audioUrl, - underlinedWords: Object.keys(parentWords), - hints: Object.fromEntries( - Object.entries(parentWords).map(([word, data]) => [ - word, - data.kn.audio_url, - ]) - ), + sentence: contentSourceData?.text || "", + audio: contentSourceData?.audioUrl || "", + underlinedWords: parentWords ? Object.keys(parentWords) : [], + hints: parentWords + ? Object.fromEntries( + Object.entries(parentWords).map(([word, data]) => [ + word, + data?.[multilingualLangCode]?.audio_url || "", + ]) + ) + : {}, }, ]; @@ -661,7 +686,7 @@ const FluencyP1 = ({ style={{ width: "90%", maxWidth: "1500px", - height: "400px", + height: "460px", background: "#fff", borderRadius: "12px", boxShadow: "0px 2px 8px rgba(0,0,0,0.1)", @@ -670,6 +695,7 @@ const FluencyP1 = ({ alignItems: "center", justifyContent: "space-between", padding: "10px", + paddingBottom: "40px", position: "relative", }} > @@ -738,6 +764,7 @@ const FluencyP1 = ({ hints={currentSentence.hints} showUnderlines={showExtras} onWordHover={handleWordHover} + lang={lang} /> { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + const sentencesData = [ { id: 1, - sentence: contentSourceData.text, - audio: contentSourceData.audioUrl, - underlinedWords: Object.keys(parentWords), - hints: Object.fromEntries( - Object.entries(parentWords).map(([word, data]) => [ - word, - data.kn.audio_url, - ]) - ), + sentence: contentSourceData?.text || "", + audio: contentSourceData?.audioUrl || "", + underlinedWords: parentWords ? Object.keys(parentWords) : [], + hints: parentWords + ? Object.fromEntries( + Object.entries(parentWords).map(([word, data]) => [ + word, + data?.[multilingualLangCode]?.audio_url || "", + ]) + ) + : {}, }, ]; @@ -270,9 +292,14 @@ const FluencyP2 = ({ currentPracticeStep = progressDatas?.currentPracticeStep; } - const currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; + let currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; let apiLevel = `M${level}-${currentLevel}`; + if (level >= 4 && level <= 9) { + currentLevel = practiceSteps?.[currentPracticeStep]?.name; + apiLevel = `M${level}-${currentLevel}`; + } + const callTelemetry = async () => { const sessionId = getLocalData("sessionId"); const responseStartTime = new Date().getTime(); diff --git a/src/components/Practice/FluencyP3.jsx b/src/components/Practice/FluencyP3.jsx index 5dd4c9c0..7dd8adca 100644 --- a/src/components/Practice/FluencyP3.jsx +++ b/src/components/Practice/FluencyP3.jsx @@ -34,7 +34,6 @@ import { callTelemetryApi, } from "../../utils/apiUtil"; import AudioTooltipModal from "./AudioTooltipModal"; -import { loadTranscriber } from "../../utils/transcriber"; import { doubleMetaphone } from "double-metaphone"; import correctSound from "../../assets/correct.wav"; import wrongSound from "../../assets/audio/wrong.wav"; @@ -134,26 +133,72 @@ const FluencyP3 = ({ const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); const wordList = [ - "Ebullient", - "Obfuscate", - "Ubiquitous", - "Pernicious", - "Vicissitude", - "Ephemeral", - "Loquacious", - "Recalcitrant", - "Pulchritudinous", - "Sagacious", - "Munificent", - "Perfunctory", - "Inscrutable", - "Quintessential", - "Ineffable", - "Perspicacious", - "Obstreperous", - "Intransigent", - "Mellifluous", - "Supercilious", + "write", + "them", + "they", + "let", + "these", + "words", + "from", + "one", + "which", + "class", + "there", + "water", + "friend", + "food", + "how", + "like", + "tree", + "give", + "help", + "very", + "unit", + "picture", + "animal", + "teacher", + "house", + "day", + "make", + "said", + "read", + "use", + "activity", + "after", + "follow", + "school", + "bird", + "many", + "question", + "below", + "play", + "why", + "here", + "should", + "sentence", + "answer", + "into", + "observe", + "plant", + "small", + "boy", + "teeth", + "new", + "more", + "story", + "lesson", + "name", + "game", + "get", + "poem", + "sea", + "eat", + "people", + "down", + "put", + "thing", + "then", + "place", ]; const [selected, setSelected] = useState(() => { @@ -199,9 +244,14 @@ const FluencyP3 = ({ currentPracticeStep = progressDatas?.currentPracticeStep; } - const currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; + let currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; let apiLevel = `M${level}-${currentLevel}`; + if (level >= 4 && level <= 9) { + currentLevel = practiceSteps?.[currentPracticeStep]?.name; + apiLevel = `M${level}-${currentLevel}`; + } + const callTelemetry = async () => { const sessionId = getLocalData("sessionId"); const responseStartTime = new Date().getTime(); @@ -599,7 +649,7 @@ const FluencyP3 = ({ display: "flex", flexDirection: "column", alignItems: "center", - padding: "0px 20px 20px 20px", + padding: "0px 20px 10px 20px", position: "relative", overflow: "hidden", }} @@ -615,12 +665,17 @@ const FluencyP3 = ({ {showResultScreen ? (

{/* Top Title Row */} @@ -629,21 +684,21 @@ const FluencyP3 = ({ display: "flex", alignItems: "center", justifyContent: "center", - gap: isMobile ? "6px" : "12px", - marginBottom: isMobile ? "10px" : "20px", + gap: isMobile ? "6px" : "8px", + marginBottom: isMobile ? "5px" : "8px", }} > speed meter

{/* Slow */}
Slow @@ -697,8 +753,8 @@ const FluencyP3 = ({ {/* Medium */}
Medium @@ -729,8 +785,8 @@ const FluencyP3 = ({ {/* Fast */}
Fast @@ -770,10 +826,11 @@ const FluencyP3 = ({ setShowResultScreen(false); }} style={{ - marginTop: isMobile ? "10px" : "16px", - marginBottom: isMobile ? "10px" : "15px", - width: isMobile ? "40px" : "50px", + marginTop: isMobile ? "5px" : "8px", + marginBottom: isMobile ? "5px" : "8px", + width: isMobile ? "38px" : "42px", cursor: "pointer", + alignSelf: "center", }} />
diff --git a/src/components/Practice/FluencyP4.jsx b/src/components/Practice/FluencyP4.jsx index 3e722f4f..74e6bfc0 100644 --- a/src/components/Practice/FluencyP4.jsx +++ b/src/components/Practice/FluencyP4.jsx @@ -31,6 +31,7 @@ import { getLocalData, setLocalData, } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import { phoneticMatch } from "../../utils/phoneticUtils"; import SpeechRecognition, { useSpeechRecognition, @@ -44,7 +45,6 @@ import { callTelemetryApi, } from "../../utils/apiUtil"; import AudioTooltipModal from "./AudioTooltipModal"; -import { loadTranscriber } from "../../utils/transcriber"; import { doubleMetaphone } from "double-metaphone"; import correctSound from "../../assets/correct.wav"; import hintimg from "../../assets/hintsicon.svg"; @@ -111,16 +111,17 @@ const UnderlinedSentence = ({ hints, showUnderlines, onWordHover, + lang, }) => { const words = sentence.split(" "); return (

{ + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + const buildSentencesData = (apiData) => { return apiData?.map((item, index) => { const sentence = item?.contentSourceData[0].text; @@ -386,7 +409,8 @@ const FluencyP4 = ({ const underlinedWords = Object?.keys(multilingualData); const hints = underlinedWords?.reduce((acc, word) => { - acc[word] = multilingualData[word]?.kn?.audio_url || null; + acc[word] = + multilingualData[word]?.[multilingualLangCode]?.audio_url || null; return acc; }, {}); @@ -410,6 +434,7 @@ const FluencyP4 = ({ const handleStart = () => { setStartTime(Date.now()); setSpeed(null); + setElapsedTime(null); }; useEffect(() => { @@ -444,7 +469,8 @@ const FluencyP4 = ({ const handleStop = () => { if (!startTime) return; const duration = (Date.now() - startTime) / 1000; - if (duration <= 30) { + setElapsedTime(Math.round(duration)); + if (duration <= TIMER_DURATION_SECONDS) { setSpeed("Fast"); } else { setSpeed("Slow"); @@ -462,9 +488,14 @@ const FluencyP4 = ({ currentPracticeStep = progressDatas?.currentPracticeStep; } - const currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; + let currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; let apiLevel = `M${level}-${currentLevel}`; + if (level >= 4 && level <= 9) { + currentLevel = practiceSteps?.[currentPracticeStep]?.name; + apiLevel = `M${level}-${currentLevel}`; + } + const callTelemetry = async () => { const sessionId = getLocalData("sessionId"); const responseStartTime = new Date().getTime(); @@ -743,7 +774,10 @@ const FluencyP4 = ({ marginBottom: "10px", }} > - +

)} @@ -763,6 +797,7 @@ const FluencyP4 = ({ hints={currentSentence.hints} showUnderlines={showExtras} onWordHover={handleWordHover} + lang={lang} /> {hoveredWord && currentSentence?.hints[hoveredWord] && ( @@ -1030,7 +1065,10 @@ const FluencyP4 = ({ alt="book" style={{ width: "30px", height: "25px" }} /> - You read 5 sentences in 30 seconds + + You read {sentencesData.length} sentences in{" "} + {TIMER_DURATION_SECONDS} seconds +
{ + const browserLangMap = { + en: "en-US", + hi: "hi-IN", + te: "te-IN", + ka: "kn-IN", + kn: "kn-IN", + ta: "ta-IN", + }; + return browserLangMap[langCode] || "en-US"; + }; + useEffect(() => { transcriptRef.current = transcript; const similarity = getSimilarity(transcript, currentSentence.sentence); setIsMatch(similarity >= 0.6); }, [transcript]); + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + const sentencesData = [ { id: 1, - sentence: contentSourceData.text, - audio: contentSourceData.audioUrl, - underlinedWords: Object.keys(parentWords), - hints: Object.fromEntries( - Object.entries(parentWords).map(([word, data]) => [ - word, - data.kn.audio_url, - ]) - ), + sentence: contentSourceData?.text || "", + audio: contentSourceData?.audioUrl || "", + underlinedWords: parentWords ? Object.keys(parentWords) : [], + hints: parentWords + ? Object.fromEntries( + Object.entries(parentWords).map(([word, data]) => [ + word, + data?.[multilingualLangCode]?.audio_url || "", + ]) + ) + : {}, }, ]; @@ -424,8 +454,25 @@ const FluencyP5 = ({ }, [showContent]); const handlePauseClick = () => { + // Capture transcript BEFORE stopping + const currentTranscript = ( + transcript || + transcriptRef.current || + "" + ).trim(); + + // Stop listening SpeechRecognition.stopListening(); - setFinalTranscript(transcriptRef.current); + const finalSimilarity = getSimilarity( + currentTranscript, + currentSentence.sentence + ); + const finalIsMatch = finalSimilarity >= 0.6; + + // Update isMatch state with final calculation + setIsMatch(finalIsMatch); + + setFinalTranscript(currentTranscript); setPaused(true); setShowBearDance(true); setShowConfetti(true); @@ -436,7 +483,6 @@ const FluencyP5 = ({ setTimeout(() => { setShowConfetti(false); setShowBearDance(false); - setShowFinalState(true); setShowResult(true); }, 3000); @@ -452,12 +498,16 @@ const FluencyP5 = ({ }; const handleSpeakClick = () => { + const lang = getLocalData("lang") || "en"; + setIsSpeaking(true); setShowContent(true); resetTranscript(); + SpeechRecognition.startListening({ continuous: true, interimResults: true, + language: getBrowserLanguage(lang), }); }; @@ -483,9 +533,14 @@ const FluencyP5 = ({ currentPracticeStep = progressDatas?.currentPracticeStep; } - const currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; + let currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; let apiLevel = `M${level}-${currentLevel}`; + if (level >= 4 && level <= 9) { + currentLevel = practiceSteps?.[currentPracticeStep]?.name; + apiLevel = `M${level}-${currentLevel}`; + } + const callTelemetry = async () => { const sessionId = getLocalData("sessionId"); const responseStartTime = new Date().getTime(); @@ -503,12 +558,9 @@ const FluencyP5 = ({ }; const playWordAudio = (audio) => { - console.log("playWordAudio called with:", audio, audioRefs.current); - if (!audio || !audioRefs.current) return; if (!audioRefs.current.paused) { - console.log("Already playing, skipping..."); return; } @@ -518,7 +570,6 @@ const FluencyP5 = ({ .play() .then(() => { setIsPlaying(true); - console.log("Playing word audio once:", audio); }) .catch((error) => { console.error("Error playing audio:", error); @@ -750,7 +801,7 @@ const FluencyP5 = ({ ) : ( - Please try again, your speech didn’t match enough + Please try again, your speech didn't match enough )}
diff --git a/src/components/Practice/LetterHuntMechanics.jsx b/src/components/Practice/LetterHuntMechanics.jsx new file mode 100644 index 00000000..6f4348a8 --- /dev/null +++ b/src/components/Practice/LetterHuntMechanics.jsx @@ -0,0 +1,1384 @@ +import React, { useState, useEffect, useRef } from "react"; +import { useNavigate } from "react-router-dom"; +import { Box, CircularProgress } from "@mui/material"; +import MainLayout from "../Layouts.jsx/MainLayout"; +import { + getLocalData, + setLocalData, + practiceSteps, + levelGetContent, +} from "../../utils/constants"; +import { + addLesson, + addPointer, +} from "../../services/orchestration/orchestrationService"; +import { getF1FlowStep, advanceF1Flow, F1_FLOW } from "../../RFlow/F1"; +import { getF2FlowStep, advanceF2Flow, F2_FLOW } from "../../RFlow/F2"; +import { F3_FLOW } from "../../RFlow/F3"; + +// Import from library +import { + LetterGame, + LanguageProvider, + AudioLanguageProvider, + sessionManager, + sessionTelemetryManager, +} from "../../lib/axl-explorations/src/lib/index"; + +/** + * Wrapper component that integrates axl-explorations LetterGame + * into the Practice.jsx mechanics system + */ +const LetterHuntMechanicsContent = ({ + isAlphabetDemoActive, + page, + setPage, + level, // Letter hunt game level (1, 2, 3, etc.) - start level + header, + points, + steps, + currentStep, + progressData, + showProgress, + background, + handleNext, + handleBack, + enableNext, + setEnableNext, + isShowCase, + loading, + setOpenMessageDialog, + vocabCount, + wordCount, + showTimer, + milestoneLevel, // Milestone level (B, 1, 2, etc.) - passed from Practice.jsx + endLevel, // Optional: end level for level range + startShowCase, // Optional: startShowCase state + setStartShowCase, // Optional: setStartShowCase function + setProgressData, // Optional: function to update progressData state in parent + setCurrentQuestion, // Optional: function to reset currentQuestion state in parent + setPoints, // Optional: function to update points state in parent + applyStep, // Optional: Apply step number (1, 2, or 3) for F1 flow + failRedirect, // Optional: Redirect target on failure (e.g., "L1", "L4", "L7") + passRedirect, // Optional: Redirect target on pass (e.g., "L4", "L7", "F2") + isF1FlowActive, // Optional: Whether F1 flow is active + f1FlowStep, // Optional: Current F1 flow step info + isF2FlowActive, // Optional: Whether F2 flow is active + f2FlowStep, // Optional: Current F2 flow step info + customLetters, // Optional: Custom letters to use for Letter Hunt (from F1/F2 config) + confidentLetters, // Optional: Letters user is confident with (appear less frequently) +}) => { + // Store the current level being played for failure handling + const [currentGameLevel, setCurrentGameLevel] = useState(1); + const [isGameComplete, setIsGameComplete] = useState(false); + const [sessionInitialized, setSessionInitialized] = useState(false); + const a3PassHandledRef = useRef(false); + const navigate = useNavigate(); + + // Track step start time for duration calculation + const [stepStartTime, setStepStartTime] = useState(null); + + // Reset step start time when step changes + useEffect(() => { + setStepStartTime(Date.now()); + }, [f1FlowStep?.index, f2FlowStep?.index, isF1FlowActive, isF2FlowActive]); + + // Helper function to get step title from flow index + const getStepTitleFromFlowIndex = (flowIndex, flowType) => { + const lang = getLocalData("lang") || "en"; + + if (flowType === "F1") { + const f1Config = levelGetContent[lang]?.["F1"]; + const stepConfig = f1Config?.[flowIndex]; + if (stepConfig?.title) { + return stepConfig.title; + } + // Fallback: construct from F1_FLOW + const flowStep = F1_FLOW[flowIndex]; + if (flowStep) { + return `${flowStep.type}${flowStep.step}`; + } + } else if (flowType === "F2") { + const f2Config = levelGetContent[lang]?.["F2"]; + const stepConfig = f2Config?.[flowIndex]; + if (stepConfig?.title) { + return stepConfig.title; + } + // Fallback: construct from F2_FLOW + const flowStep = F2_FLOW[flowIndex]; + if (flowStep) { + return `${flowStep.type}${flowStep.step}`; + } + } else if (flowType === "F3") { + const f3Config = levelGetContent[lang]?.["F3"]; + const stepConfig = f3Config?.[flowIndex]; + if (stepConfig?.title) { + return stepConfig.title; + } + // Fallback: construct from F3_FLOW + const flowStep = F3_FLOW[flowIndex]; + if (flowStep) { + return `${flowStep.type}${flowStep.step}`; + } + } + return undefined; + }; + + // Helper function to calculate duration in seconds + const calculateDuration = () => { + if (!stepStartTime) return undefined; + return Math.round((Date.now() - stepStartTime) / 1000); // Duration in seconds + }; + + // Calculate skipPreview: show demo only for F1 P1 and F2 P1 + const skipPreview = React.useMemo(() => { + const currentF1Step = getF1FlowStep(); + const currentF2Step = getF2FlowStep(); + const isF1P1 = currentF1Step.index === 1; // F1 P1 + const isF2P1 = currentF2Step.index === 1; // F2 P1 + return !(isF1P1 || isF2P1); // Skip preview if NOT F1 P1 or F2 P1 + }, []); // Empty deps - only calculate once on mount + + // Handle A3 pass - redirect to discovery start (for F1) or next flow (for F2) + const handleA3Pass = async () => { + if (a3PassHandledRef.current) { + return; + } + a3PassHandledRef.current = true; + + if (isF1FlowActive) { + console.log("F1 A3 passed - saving progress and redirecting to F2"); + + // Save F1 A3 completion progress before transitioning to F2 + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + // Clear F1 flow index to reset for F2 + setLocalData("f1FlowIndex", null); + + // IMPORTANT: Reset F2 flow index to 0 to start from F2-A1 (not F2-A3) + // This ensures F2 starts fresh after F1 completes + setLocalData("f2FlowIndex", 0); + + // Save F2 progress as lesson 1 (index 0) to ensure backend knows to start F2 from beginning + try { + const f2TotalSteps = F2_FLOW.length; + const f2Progress = Math.round(((0 + 1) / f2TotalSteps) * 100); // Index 0 = lesson 1, progress = 1/21 * 100 + + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: "1", // F2 starts at index 0, so 1-indexed is 1 + progress: f2Progress, + language: lang, + milestoneLevel: "B", + subMilestoneLevel: "F2", + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex(0, "F2"), + }); + console.log( + "F2 flow initialized at lesson 1 (index 0) after F1 completion" + ); + } catch (error) { + console.error("Error initializing F2 flow progress:", error); + } + + // Clear practice progress for F2 to start fresh + setLocalData("practiceProgress", null); + + // Also clear any F2 flow completion flags + setLocalData("f2FlowComplete", null); + + console.log( + "F1 A3 passed - F1 flow cleared, F2 flow reset to index 0, redirecting to discovery start" + ); + // Redirect to discovery start (which will detect F2 flow and start from F2-A1) + navigate("/discover-start"); + } else if (isF2FlowActive) { + console.log("F2 A3 passed - saving progress and redirecting to F3"); + + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + setLocalData("f2FlowIndex", null); + setLocalData("f2FlowComplete", "true"); + setLocalData("f3FlowIndex", 0); + + if (sessionId && lang) { + try { + const f3TotalSteps = F3_FLOW.length; + const f3Progress = Math.round(((0 + 1) / f3TotalSteps) * 100); // Index 0 = lesson 1 + + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: "1", // F3 starts at index 0, so 1-indexed is 1 + progress: f3Progress, + language: lang, + milestoneLevel: "B", + subMilestoneLevel: "F3", + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex(0, "F3"), + }); + console.log( + "F3 flow initialized at lesson 1 (index 0) after F2 completion" + ); + } catch (error) { + console.error("Error initializing F3 flow progress:", error); + } + } + + // Clear practice progress for F3 to start fresh + setLocalData("practiceProgress", null); + + console.log( + "F2 A3 passed - F2 flow cleared, F3 flow initialized to index 0, redirecting to discover-start" + ); + // Redirect to discover-start (which will detect F3 flow and start from F3-P1) + navigate("/discover-start"); + } + }; + + // Get F1 flow assessment parameters from config + const getF1AssessmentParams = () => { + if (!isF1FlowActive || !f1FlowStep?.step) { + return { + sub_session_id: undefined, + sub_milestone_level: undefined, + apply_level: undefined, + sub_apply_level: undefined, + }; + } + + // Get sub_session_id from telemetry + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sub_session_id = currentSubSession?.subSessionId; + + // For F1 flow, sub_milestone_level is always "F1" + const sub_milestone_level = "F1"; + + // Get step title from F1 config + const lang = getLocalData("lang") || "en"; + const f1Config = levelGetContent[lang]?.["F1"]; + const f1StepConfig = + f1Config && Array.isArray(f1Config) && f1Config[f1FlowStep.index] + ? f1Config[f1FlowStep.index] + : null; + const stepTitle = + f1StepConfig?.title || + (f1FlowStep.step?.type === "A" + ? `A${f1FlowStep.step?.step}` + : f1FlowStep.step?.type === "P" + ? `P${f1FlowStep.step?.step}` + : f1FlowStep.step?.type === "L" + ? `L${f1FlowStep.step?.step}` + : null); + + // Determine apply_level - use step title for all F1 flow steps + // For Apply steps, this will be "A1", "A2", "A3" + // For Practice steps, this will be "P1", "P2", etc. (though backend might only use it for Apply steps) + const apply_level = stepTitle || undefined; + + // sub_apply_level will be passed dynamically based on currentGameLevel + // This represents the level within the Apply step (1, 2, or 3) + + return { + sub_session_id, + sub_milestone_level, + apply_level, + // sub_apply_level will be set dynamically when calling LetterGame + }; + }; + + // Get F2 flow assessment parameters from config + const getF2AssessmentParams = () => { + if (!isF2FlowActive || !f2FlowStep?.step) { + return { + sub_session_id: undefined, + sub_milestone_level: undefined, + apply_level: undefined, + sub_apply_level: undefined, + }; + } + + // Get sub_session_id from telemetry + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sub_session_id = currentSubSession?.subSessionId; + + // For F2 flow, sub_milestone_level is always "F2" + const sub_milestone_level = "F2"; + + // Get step title from F2 config + const lang = getLocalData("lang") || "en"; + const f2Config = levelGetContent[lang]?.["F2"]; + const f2StepConfig = + f2Config && Array.isArray(f2Config) && f2Config[f2FlowStep.index] + ? f2Config[f2FlowStep.index] + : null; + const stepTitle = + f2StepConfig?.title || + (f2FlowStep.step?.type === "A" + ? `A${f2FlowStep.step?.step}` + : f2FlowStep.step?.type === "P" + ? `P${f2FlowStep.step?.step}` + : f2FlowStep.step?.type === "L" + ? `L${f2FlowStep.step?.step}` + : null); + + // Determine apply_level - use step title for all F2 flow steps + // For Apply steps, this will be "A1", "A2", "A3" + // For Practice steps, this will be "P1", "P2", etc. (though backend might only use it for Apply steps) + const apply_level = stepTitle || undefined; + + // sub_apply_level will be passed dynamically based on currentGameLevel + // This represents the level within the Apply step (1, 2, or 3) + + return { + sub_session_id, + sub_milestone_level, + apply_level, + // sub_apply_level will be set dynamically when calling LetterGame + }; + }; + + // Get assessment params based on active flow + const f1AssessmentParams = getF1AssessmentParams(); + const f2AssessmentParams = getF2AssessmentParams(); + + // Use F2 params if F2 is active, otherwise use F1 params, or default empty object + const assessmentParams = isF2FlowActive + ? f2AssessmentParams + : isF1FlowActive + ? f1AssessmentParams + : { + sub_session_id: undefined, + sub_milestone_level: undefined, + apply_level: undefined, + }; + + // Initialize telemetry session before game starts + useEffect(() => { + const initializeSession = async () => { + try { + const currentUser = sessionManager.getCurrentUser(); + let userId = "anonymous"; + + if (currentUser && currentUser.username) { + userId = currentUser.username; + } else { + const storedUser = + localStorage.getItem("user") || localStorage.getItem("username"); + if (storedUser) { + userId = storedUser; + } + } + + const currentSession = sessionTelemetryManager.getCurrentSession(); + if (!currentSession || !currentSession.isActive) { + await sessionTelemetryManager.startUserSession(userId); + console.log("✅ Telemetry session initialized for Letter Hunt game"); + } + + setSessionInitialized(true); + } catch (error) { + console.warn("Failed to initialize telemetry session:", error); + setSessionInitialized(true); + } + }; + + // Add timeout fallback to ensure session initializes even if there's an error + const timeoutId = setTimeout(() => { + console.warn("Session initialization timeout - proceeding anyway"); + setSessionInitialized(true); + }, 3000); // 3 second timeout + + // Add class to body to prevent scrolling + document.body.classList.add("letter-hunt-active"); + + initializeSession(); + + // Cleanup: remove class when component unmounts and clear timeout + return () => { + document.body.classList.remove("letter-hunt-active"); + clearTimeout(timeoutId); + }; + }, []); + + const handleGameBack = () => { + if (handleBack) { + handleBack(); + } + }; + + // Helper function to map redirect string (e.g., "L1", "L4", "L7") to F1 flow index + const getF1FlowIndexFromRedirect = (redirect) => { + if (!redirect) return null; + + // Map Learn step redirects to F1 flow indices + // F1_FLOW: L1(0), P1(1), L2(2), P2(3), L3(4), P3(5), A1(6), L4(7), P4(8), L5(9), P5(10), L6(11), P6(12), A2(13), L7(14), P7(15), L8(16), P8(17), L9(18), P9(19), A3(20) + const redirectMap = { + L1: 0, // Learn 1 + L2: 2, // Learn 2 + L3: 4, // Learn 3 + L4: 7, // Learn 4 + L5: 9, // Learn 5 + L6: 11, // Learn 6 + L7: 14, // Learn 7 + L8: 16, // Learn 8 + L9: 18, // Learn 9 + }; + + return redirectMap[redirect] !== undefined ? redirectMap[redirect] : null; + }; + + // Helper function to map redirect string (e.g., "L1", "L4", "L7") to F2 flow index + const getF2FlowIndexFromRedirect = (redirect) => { + if (!redirect) return null; + + // Map Learn step redirects to F2 flow indices + // F2_FLOW: L1(0), P1(1), L2(2), P2(3), L3(4), P3(5), A1(6), L4(7), P4(8), L5(9), P5(10), L6(11), P6(12), A2(13), L7(14), P7(15), L8(16), P8(17), L9(18), P9(19), A3(20) + const redirectMap = { + L1: 0, // Learn 1 + L2: 2, // Learn 2 + L3: 4, // Learn 3 + L4: 7, // Learn 4 + L5: 9, // Learn 5 + L6: 11, // Learn 6 + L7: 14, // Learn 7 + L8: 16, // Learn 8 + L9: 18, // Learn 9 + }; + + return redirectMap[redirect] !== undefined ? redirectMap[redirect] : null; + }; + + // Backward compatibility: handleLevel1Failure for level 1 only + const handleLevel1Failure = async () => { + return handleLevelFailure(1); + }; + + // Handle level failure in showcase mode - redirect based on Apply step rules + // For Apply steps: + // - Level 1 or 2 fail: go to failRedirect (e.g., "L1" for Apply 1) + // - Level 3 fail: go to failRedirect (e.g., "L1" for Apply 1) + // - Level 3 pass: go to passRedirect (e.g., "L4" for Apply 1) + const handleLevelFailure = async (failedLevel) => { + console.log( + "handleLevelFailure called - failedLevel:", + failedLevel, + "isShowCase:", + isShowCase, + "applyStep:", + applyStep, + "failRedirect:", + failRedirect, + "isF1FlowActive:", + isF1FlowActive, + "isF2FlowActive:", + isF2FlowActive + ); + try { + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + let targetStep = 0; // Default to P1 + let targetFlowIndex = null; + + // If this is an Apply step with a failRedirect, use it + // Check for F1 flow first + if (applyStep && failRedirect && isF1FlowActive) { + targetFlowIndex = getF1FlowIndexFromRedirect(failRedirect); + if (targetFlowIndex !== null) { + // Set F1 flow index to the target Learn step + setLocalData("f1FlowIndex", targetFlowIndex); + targetStep = targetFlowIndex; // Use flow index as practice step index for F1 + console.log( + `F1 Apply step ${applyStep} - Level ${failedLevel} failed - redirecting to ${failRedirect} (F1 flow index ${targetFlowIndex})` + ); + } else { + console.error( + `Failed to map failRedirect "${failRedirect}" to F1 flow index` + ); + } + } + // Check for F2 flow + else if (applyStep && failRedirect && isF2FlowActive) { + targetFlowIndex = getF2FlowIndexFromRedirect(failRedirect); + if (targetFlowIndex !== null) { + // Set F2 flow index to the target Learn step + setLocalData("f2FlowIndex", targetFlowIndex); + targetStep = targetFlowIndex; // Use flow index as practice step index for F2 + console.log( + `F2 Apply step ${applyStep} - Level ${failedLevel} failed - redirecting to ${failRedirect} (F2 flow index ${targetFlowIndex})` + ); + } else { + console.error( + `Failed to map failRedirect "${failRedirect}" to F2 flow index` + ); + } + } else { + console.warn("handleLevelFailure - Missing required conditions:", { + applyStep: !!applyStep, + failRedirect: !!failRedirect, + isF1FlowActive: !!isF1FlowActive, + isF2FlowActive: !!isF2FlowActive, + }); + } + + // If we have a valid targetFlowIndex, proceed with redirect + if (targetFlowIndex !== null) { + // For F1/F2 flows, use F1_FLOW.length or F2_FLOW.length instead of practiceSteps.length + // Ensure progress doesn't exceed 100% + const totalSteps = isF1FlowActive + ? F1_FLOW.length + : isF2FlowActive + ? F2_FLOW.length + : practiceSteps?.length || 21; + const calculatedProgress = (targetStep + 1 / totalSteps) * 100; + const currentPracticeProgress = Math.min( + 100, + Math.round(calculatedProgress) + ); + + // Update learner progress via addLesson + // Convert to 1-indexed for backend + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: (targetStep + 1).toString(), // Convert to 1-indexed for backend + progress: currentPracticeProgress, + language: lang, + milestoneLevel: milestoneLevel || "B", + subMilestoneLevel: isF1FlowActive + ? "F1" + : isF2FlowActive + ? "F2" + : undefined, + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex( + targetStep, + isF1FlowActive ? "F1" : "F2" + ), + }); + + // Update local storage + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: currentPracticeProgress, + currentPracticeStep: targetStep, + }; + setLocalData( + "practiceProgress", + JSON.stringify(updatedPracticeProgress) + ); + console.log("Updated localStorage with:", updatedPracticeProgress); + + // Update parent state if setProgressData is provided + if (setProgressData && typeof setProgressData === "function") { + setProgressData(updatedPracticeProgress); + console.log("Updated progressData state"); + } + + // Reset currentQuestion state in parent to 0 so handleNext doesn't increment + if (setCurrentQuestion && typeof setCurrentQuestion === "function") { + setCurrentQuestion(0); + console.log("Reset currentQuestion to 0"); + } + + // Clear any F1/F2 flow advancement flag to ensure handleNext processes the redirect + if (isF1FlowActive) { + setLocalData("f1FlowAdvancedByLetterHunt", "false"); + } else if (isF2FlowActive) { + setLocalData("f2FlowAdvancedByLetterHunt", "false"); + } + + // Use a small delay to ensure localStorage and state are updated before handleNext reads them + setTimeout(() => { + console.log("Calling handleNext to exit and redirect to Learn step"); + if (handleNext) { + handleNext(false); + } else if (handleBack) { + handleBack(); + } + }, 100); + } else { + console.error( + "handleLevelFailure - No valid targetFlowIndex, cannot redirect" + ); + // Still try to exit the game + setTimeout(() => { + if (handleNext) { + handleNext(false); + } else if (handleBack) { + handleBack(); + } + }, 100); + } + } catch (error) { + console.error("Error redirecting after level failure:", error); + // Still exit the game even if progress update fails + setTimeout(() => { + if (handleNext) { + handleNext(false); + } else if (handleBack) { + handleBack(); + } + }, 100); + } + }; + + // Handle level completion - update learner progress and exit + const handleLevelComplete = async (completedLevel) => { + try { + console.log("handleLevelComplete called:", { + completedLevel, + isShowCase, + applyStep, + endLevel, + passRedirect, + isF1FlowActive, + isF2FlowActive, + }); + + // For Apply steps in showcase mode, check if all levels are complete + if (isShowCase && applyStep && endLevel && completedLevel >= endLevel) { + // All levels passed - redirect to passRedirect + console.log( + `Apply step ${applyStep} completed all levels (${completedLevel}/${endLevel}) - will redirect to ${passRedirect} after success screen` + ); + + // Add points for Apply step completion (all 3 levels completed) + // This must happen BEFORE redirect logic to ensure points are added for all Apply steps + if (!localStorage.getItem("contentSessionId")) { + try { + const lang = getLocalData("lang") || "en"; + let pointsToAdd = 30; // Default for Apply steps + + if (isF1FlowActive) { + const f1Config = levelGetContent[lang]?.["F1"]; + const currentF1FlowStep = getF1FlowStep(); + const completedStepContent = f1Config?.[currentF1FlowStep.index]; + pointsToAdd = completedStepContent?.contentCount || 30; + } else if (isF2FlowActive) { + const f2Config = levelGetContent[lang]?.["F2"]; + const currentF2FlowStep = getF2FlowStep(); + const completedStepContent = f2Config?.[currentF2FlowStep.index]; + pointsToAdd = completedStepContent?.contentCount || 30; + } + + const result = await addPointer(pointsToAdd, "B"); + + if ( + result?.result?.totalLanguagePoints !== undefined && + setPoints + ) { + setPoints(result.result.totalLanguagePoints); + } + } catch (error) { + console.error("Error adding Apply step points:", error); + } + } + + // Store redirect info to execute after success screen is shown + const executeRedirect = async () => { + if (passRedirect === "F2" || passRedirect === "F3") { + if (!a3PassHandledRef.current) { + await handleA3Pass(); + } else { + navigate("/discover-start"); + } + return; + } + + // For F1 flow, redirect to the specified Learn step + if (isF1FlowActive) { + const targetFlowIndex = getF1FlowIndexFromRedirect(passRedirect); + if (targetFlowIndex !== null) { + setLocalData("f1FlowIndex", targetFlowIndex); + const targetStep = targetFlowIndex; + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + // Calculate progress using F1_FLOW.length, matching F1's pattern + // Ensure progress doesn't exceed 100% + const calculatedProgress = + ((targetStep + 1) / F1_FLOW.length) * 100; + const cappedProgress = Math.min( + 100, + Math.round(calculatedProgress) + ); + + // Convert to 1-indexed for backend + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: (targetStep + 1).toString(), + progress: cappedProgress, + language: lang, + milestoneLevel: milestoneLevel, + subMilestoneLevel: "F1", + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex(targetStep, "F1"), + }); + + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: cappedProgress, + currentPracticeStep: targetStep, + }; + setLocalData( + "practiceProgress", + JSON.stringify(updatedPracticeProgress) + ); + + if (setProgressData && typeof setProgressData === "function") { + setProgressData(updatedPracticeProgress); + } + + if ( + setCurrentQuestion && + typeof setCurrentQuestion === "function" + ) { + setCurrentQuestion(0); + } + + // Set a flag to indicate F1 flow was already advanced by LetterHuntMechanics + // This prevents handleNext from making duplicate addLesson calls + setLocalData("f1FlowAdvancedByLetterHunt", "true"); + + // Delay redirect to allow success screen to show first (4 seconds) + setTimeout(() => { + if (handleNext) { + handleNext(false); + } + // Clear the flag after a short delay + setTimeout(() => { + setLocalData("f1FlowAdvancedByLetterHunt", "false"); + }, 500); + }, 4000); // 4 second delay to ensure success screen is visible + return; + } + } + + // For F2 flow, redirect to the specified Learn step + if (isF2FlowActive) { + const targetFlowIndex = getF2FlowIndexFromRedirect(passRedirect); + if (targetFlowIndex !== null) { + console.log( + `F2 Apply step ${applyStep} passed - redirecting to ${passRedirect} (F2 flow index ${targetFlowIndex})` + ); + setLocalData("f2FlowIndex", targetFlowIndex); + const targetStep = targetFlowIndex; + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + // Calculate progress using F2_FLOW.length, matching F1's pattern + // Ensure progress doesn't exceed 100% + const calculatedProgress = + ((targetStep + 1) / F2_FLOW.length) * 100; + const cappedProgress = Math.min( + 100, + Math.round(calculatedProgress) + ); + + // Convert to 1-indexed for backend + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: (targetStep + 1).toString(), + progress: cappedProgress, + language: lang, + milestoneLevel: milestoneLevel, + subMilestoneLevel: "F2", + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex(targetStep, "F2"), + }); + + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: cappedProgress, + currentPracticeStep: targetStep, + }; + setLocalData( + "practiceProgress", + JSON.stringify(updatedPracticeProgress) + ); + + if (setProgressData && typeof setProgressData === "function") { + setProgressData(updatedPracticeProgress); + } + + if ( + setCurrentQuestion && + typeof setCurrentQuestion === "function" + ) { + setCurrentQuestion(0); + } + + // Set a flag to indicate F2 flow was already advanced by LetterHuntMechanics + // This prevents handleNext from making duplicate addLesson calls + setLocalData("f2FlowAdvancedByLetterHunt", "true"); + + // Delay redirect to allow success screen to show first (4 seconds) + setTimeout(() => { + if (handleNext) { + handleNext(false); + } + // Clear the flag after a short delay + setTimeout(() => { + setLocalData("f2FlowAdvancedByLetterHunt", "false"); + }, 500); + }, 4000); // 4 second delay to ensure success screen is visible + return; + } + } + + console.warn( + `Could not redirect: passRedirect="${passRedirect}", isF1FlowActive=${isF1FlowActive}, isF2FlowActive=${isF2FlowActive}` + ); + }; + + // Execute redirect after a delay to allow success screen to render + executeRedirect(); + return; + } + + // For showcase mode with endLevel but not all levels complete yet, don't exit + // Let the user continue to the next level via the "Next Level" button + if (isShowCase && endLevel && completedLevel < endLevel) { + console.log( + `Apply step ${applyStep} - Level ${completedLevel} completed, but not all levels done (${completedLevel}/${endLevel}). Continuing to next level.` + ); + // Don't exit - let the game continue to the next level + return; + } + + // For showcase mode without endLevel or non-Apply steps, exit + if (isShowCase && (!endLevel || !applyStep)) { + // In showcase mode without endLevel, just exit without updating progress + if (handleNext) { + handleNext(); + } else if (handleBack) { + handleBack(); + } + return; + } + + // For non-showcase mode, update progress and move to next step + // Only proceed if handleNext and milestoneLevel are provided + if (!handleNext || !milestoneLevel) { + // If no handleNext, just return - completion is handled elsewhere + return; + } + + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + // Validate required fields + if (!sessionId) { + console.error( + "LetterHuntMechanics - sessionId is missing, cannot save progress" + ); + return; + } + if (!lang) { + console.error( + "LetterHuntMechanics - language is missing, cannot save progress" + ); + return; + } + + // Check if this is F2 flow - if so, advance F2 flow index instead of practice step + if (isF2FlowActive && f2FlowStep?.step) { + console.log("F2 flow Practice step completed - advancing F2 flow"); + + // Get current F2 flow step + const currentF2FlowStep = getF2FlowStep(); + console.log("Current F2 flow step before advance:", currentF2FlowStep); + + // Advance F2 flow + const nextStep = advanceF2Flow(); + console.log("advanceF2Flow returned:", nextStep); + + // Get updated F2 flow step + const updatedF2FlowStep = getF2FlowStep(); + console.log("Updated F2 flow step after advance:", updatedF2FlowStep); + + if (updatedF2FlowStep.step) { + // Update learner progress with new F2 flow index + const newF2FlowIndex = updatedF2FlowStep.index; + const totalF2Steps = F2_FLOW.length; // Total F2 flow steps + // Ensure progress doesn't exceed 100% + const calculatedProgress = + ((newF2FlowIndex + 1) / totalF2Steps) * 100; + const currentPracticeProgress = Math.min( + 100, + Math.round(calculatedProgress) + ); + + // Convert to 1-indexed for backend (same as Learn step completion) + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: (newF2FlowIndex + 1).toString(), // Convert to 1-indexed for backend + progress: currentPracticeProgress, + language: lang, + milestoneLevel: "B", + subMilestoneLevel: "F2", + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex(newF2FlowIndex, "F2"), + }); + console.log( + "F2 Practice step progress saved by LetterHuntMechanics:", + { + completedStepIndex: currentF2FlowStep.index, + nextStepIndex: newF2FlowIndex, + lessonSaved: (newF2FlowIndex + 1).toString(), // 1-indexed + progress: currentPracticeProgress, + } + ); + + // Update points for F2 flow based on contentCount + // For Apply steps (A1, A2, A3), points are added separately after all 3 levels are completed + // For other steps (L1-L7, P1-P3), add points here + if (!localStorage.getItem("contentSessionId")) { + const f2Config = levelGetContent[lang]?.["F2"]; + const completedStepContent = f2Config?.[currentF2FlowStep.index]; + const isApplyStep = completedStepContent?.title?.startsWith("A"); + + // Skip point addition for Apply steps - they're handled after all 3 levels are complete + if (!isApplyStep) { + try { + const contentCount = completedStepContent?.contentCount || 1; + const result = await addPointer(contentCount, "B"); + + if ( + result?.result?.totalLanguagePoints !== undefined && + setPoints + ) { + setPoints(result.result.totalLanguagePoints); + } + } catch (error) { + console.error("Error updating F2 flow points:", error); + } + } + } + + // Update local storage + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: currentPracticeProgress, + currentPracticeStep: newF2FlowIndex, + }; + setLocalData( + "practiceProgress", + JSON.stringify(updatedPracticeProgress) + ); + + // Update parent state if setProgressData is provided + if (setProgressData && typeof setProgressData === "function") { + setProgressData(updatedPracticeProgress); + } + + // Reset currentQuestion state to 0 so handleNext doesn't increment + if (setCurrentQuestion && typeof setCurrentQuestion === "function") { + setCurrentQuestion(0); + } + + // Set a flag to indicate F2 flow was already advanced by LetterHuntMechanics + // This prevents handleNext from advancing again + setLocalData("f2FlowAdvancedByLetterHunt", "true"); + + // Exit the game by calling handleNext (will move to next F2 flow step) + setTimeout(() => { + handleNext(false); + // Clear the flag after a short delay + setTimeout(() => { + setLocalData("f2FlowAdvancedByLetterHunt", "false"); + }, 500); + }, 100); + return; + } else { + console.error("F2 flow advance failed - no next step available"); + } + } + + // Check if this is F1 flow - if so, advance F1 flow index instead of practice step + if (isF1FlowActive && f1FlowStep?.step) { + console.log("F1 flow Practice step completed - advancing F1 flow"); + + // Get current F1 flow step + const currentF1FlowStep = getF1FlowStep(); + console.log("Current F1 flow step before advance:", currentF1FlowStep); + + // Advance F1 flow + const nextStep = advanceF1Flow(); + console.log("advanceF1Flow returned:", nextStep); + + // Get updated F1 flow step + const updatedF1FlowStep = getF1FlowStep(); + console.log("Updated F1 flow step after advance:", updatedF1FlowStep); + + // Validate that the flow advanced correctly + if (updatedF1FlowStep.index === currentF1FlowStep.index) { + console.error( + "F1 flow did not advance! Current index:", + currentF1FlowStep.index, + "Updated index:", + updatedF1FlowStep.index + ); + // Force advance if it didn't work + const forcedIndex = currentF1FlowStep.index + 1; + if (forcedIndex < F1_FLOW.length) { + setLocalData("f1FlowIndex", forcedIndex); + const forcedStep = getF1FlowStep(); + console.log("Forced F1 flow step:", forcedStep); + if (forcedStep.step) { + // Use the forced step + updatedF1FlowStep.index = forcedStep.index; + updatedF1FlowStep.step = forcedStep.step; + updatedF1FlowStep.isLast = forcedStep.isLast; + } + } + } + + if (updatedF1FlowStep.step) { + // Update learner progress with new F1 flow index + const newF1FlowIndex = updatedF1FlowStep.index; + const totalF1Steps = F1_FLOW.length; // Use actual F1_FLOW length + // Ensure progress doesn't exceed 100% + const calculatedProgress = + ((newF1FlowIndex + 1) / totalF1Steps) * 100; + const currentPracticeProgress = Math.min( + 100, + Math.round(calculatedProgress) + ); + + // Convert to 1-indexed for backend (same as Learn step completion) + // Example: P1 (index 1) completes → advances to P2 (index 2) → save lesson "3" (1-indexed) + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: (newF1FlowIndex + 1).toString(), // Convert to 1-indexed for backend + progress: currentPracticeProgress, + language: lang, + milestoneLevel: "B", + subMilestoneLevel: "F1", + duration: calculateDuration(), + applyLevel: getStepTitleFromFlowIndex(newF1FlowIndex, "F1"), + }); + console.log( + "F1 Practice step progress saved by LetterHuntMechanics:", + { + completedStepIndex: currentF1FlowStep.index, + nextStepIndex: newF1FlowIndex, + lessonSaved: (newF1FlowIndex + 1).toString(), // 1-indexed + progress: currentPracticeProgress, + } + ); + + // Update points for F1 flow based on contentCount + // For Apply steps (A1, A2, A3), points are added separately after all 3 levels are completed + // For other steps (L1-L7, P1-P3), add points here + if (!localStorage.getItem("contentSessionId")) { + const f1Config = levelGetContent[lang]?.["F1"]; + const completedStepContent = f1Config?.[currentF1FlowStep.index]; + const isApplyStep = completedStepContent?.title?.startsWith("A"); + + // Skip point addition for Apply steps - they're handled after all 3 levels are complete + if (!isApplyStep) { + try { + const contentCount = completedStepContent?.contentCount || 1; + const result = await addPointer(contentCount, "B"); + + if ( + result?.result?.totalLanguagePoints !== undefined && + setPoints + ) { + setPoints(result.result.totalLanguagePoints); + } + } catch (error) { + console.error("Error updating F1 flow points:", error); + } + } + } + + // Update local storage + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: currentPracticeProgress, + currentPracticeStep: newF1FlowIndex, + }; + setLocalData( + "practiceProgress", + JSON.stringify(updatedPracticeProgress) + ); + + // Update parent state if setProgressData is provided + if (setProgressData && typeof setProgressData === "function") { + setProgressData(updatedPracticeProgress); + } + + // Reset currentQuestion state to 0 so handleNext doesn't increment + if (setCurrentQuestion && typeof setCurrentQuestion === "function") { + setCurrentQuestion(0); + } + + // Set a flag to indicate F1 flow was already advanced by LetterHuntMechanics + // This prevents handleNext from advancing again + setLocalData("f1FlowAdvancedByLetterHunt", "true"); + + // Exit the game by calling handleNext (will move to next F1 flow step) + setTimeout(() => { + handleNext(false); + // Clear the flag after a short delay + setTimeout(() => { + setLocalData("f1FlowAdvancedByLetterHunt", "false"); + }, 500); + }, 100); + return; + } else { + console.error("F1 flow advance failed - no next step available"); + } + } + + // For non-F1 flow, use regular practice step advancement + // Get current practice step from progressData + const currentPracticeStep = progressData?.currentPracticeStep || 0; + const newPracticeStep = currentPracticeStep + 1; + + // Calculate progress percentage + // Use practiceSteps length if available, otherwise use a default + const totalSteps = practiceSteps?.length || 10; + const limit = 1; // Default limit + const currentPracticeProgress = Math.round( + (newPracticeStep / (totalSteps * limit)) * 100 + ); + + // Determine milestone type + const currentLevelTitle = practiceSteps?.[newPracticeStep]?.title || "P1"; + const milestoneType = ["S1", "S2"].includes(currentLevelTitle) + ? "showcase" + : "practice"; + + // Update learner progress via addLesson + await addLesson({ + sessionId: sessionId, + milestone: milestoneType, + lesson: newPracticeStep, + progress: currentPracticeProgress, + language: lang, + milestoneLevel: milestoneLevel, + }); + + // Update points for non-F flows based on contentCount + if (!localStorage.getItem("contentSessionId")) { + try { + // Get contentCount from current step config + const lang = getLocalData("lang") || "en"; + const levelKey = + milestoneLevel === "B" + ? "F1" + : `m${milestoneLevel?.replace("m", "") || "1"}`; + const levelConfig = levelGetContent[lang]?.[levelKey]; + const currentStepContent = levelConfig?.find( + (step) => step.title === currentLevelTitle + ); + const contentCount = currentStepContent?.contentCount || 1; + + const pointsToAdd = contentCount; + const milestone = milestoneLevel || "m1"; + + const result = await addPointer(pointsToAdd, milestone); + const awardedPoints = result?.result?.points; + + if (awardedPoints === pointsToAdd) { + console.log("LetterHunt points updated (handleLevelComplete):", { + stepTitle: currentLevelTitle, + pointsAdded: pointsToAdd, + contentCount, + totalPoints: result?.result?.totalLanguagePoints, + }); + } + } catch (error) { + console.error("Error updating points in handleLevelComplete:", error); + } + } + + // Update local storage + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: currentPracticeProgress, + currentPracticeStep: newPracticeStep, + }; + setLocalData("practiceProgress", JSON.stringify(updatedPracticeProgress)); + + // Update parent state if setProgressData is provided + if (setProgressData && typeof setProgressData === "function") { + setProgressData(updatedPracticeProgress); + } + + // Reset currentQuestion state to 0 so handleNext doesn't increment + if (setCurrentQuestion && typeof setCurrentQuestion === "function") { + setCurrentQuestion(0); + } + + // Exit the game by calling handleNext (will move to next P level) + setTimeout(() => { + handleNext(false); + }, 100); + } catch (error) { + console.error( + "Error updating learner progress after letter hunt completion:", + error + ); + // Still exit the game even if progress update fails + if (handleNext) { + setTimeout(() => { + handleNext(false); + }, 100); + } + } + }; + + // Use getLocalData("lang") as the primary source for language + // This ensures the main app's language setting takes precedence over the library's selectedLanguage + const lang = getLocalData("lang") || "en"; + const initialLanguage = + lang === "en" + ? "en" + : lang === "te" + ? "te" + : lang === "kn" + ? "kn" + : lang === "mr" + ? "mr" + : lang === "hi" + ? "hi" + : "en"; + const initialAudioLanguage = initialLanguage; + const sessionId = getLocalData("sessionId"); + useEffect(() => { + localStorage.setItem("selectedLanguage", initialLanguage); + localStorage.setItem("selectedAudioLanguage", initialAudioLanguage); + }, [initialLanguage, initialAudioLanguage]); + + if (!sessionInitialized) { + return ( + +
+

Loading game...

+
+
+ ); + } + + return ( + +
+ + + {isAlphabetDemoActive ? ( + + + + ) : ( +
+ handleLevelFailure(1)} // Backward compatibility for level 1 only + onLevelFailure={handleLevelFailure} // New callback for any level failure (includes level number) + customLetters={customLetters} // Pass customLetters from F1 config + confidentLetters={confidentLetters} // Pass confidentLetters from F1/F2 config + sub_session_id={assessmentParams.sub_session_id} // Pass sub session ID from telemetry + sub_milestone_level={assessmentParams.sub_milestone_level} // Pass "F1" or "F2" based on active flow + apply_level={assessmentParams.apply_level} // Pass apply level (A1, A2, A3) from config + onA3Pass={handleA3Pass} // Callback when A3 passes + sessionId={sessionId} + skipPreview={skipPreview} + // sub_apply_level is calculated dynamically in LetterGame based on currentLevel + /> +
+ )} +
+
+
+
+ ); +}; + +const LetterHuntMechanics = (props) => { + return ( + + ); +}; + +export default LetterHuntMechanics; diff --git a/src/components/Practice/LetterLauncher.jsx b/src/components/Practice/LetterLauncher.jsx new file mode 100644 index 00000000..5171a68e --- /dev/null +++ b/src/components/Practice/LetterLauncher.jsx @@ -0,0 +1,494 @@ +import React, { useState, useEffect, useRef } from "react"; +import MainLayout from "../Layouts.jsx/MainLayout"; +import { Box, Typography, Button, CircularProgress } from "@mui/material"; +import { getLocalData, setLocalData } from "../../utils/constants"; +import { useNavigate } from "react-router-dom"; +import { ArrowRight, RotateCcw } from "lucide-react"; +import correctSound from "../../assets/correct.wav"; +import wrongSound from "../../assets/audio/wrong.wav"; + +/** + * Letter Launcher Component + * Speed-based letter/syllable recognition game for F3 flow + * + * Props: + * - level: Starting level (1, 2, 3, etc.) + * - endLevel: Optional end level for level range (for Apply steps) + * - contentType: "letter" or "syllable" + * - contentCount: Number of items to show per level + * - isShowcase: Whether this is an Apply step (has levels and pass/fail criteria) + * - handleNext: Callback when step completes + * - handleBack: Callback for back navigation + * - applyStep: Apply step number (1 or 2) + * - failRedirect: Redirect target on failure + * - passRedirect: Redirect target on pass + * - isF3FlowActive: Whether F3 flow is active + * - f3FlowStep: Current F3 flow step info + */ +const LetterLauncher = ({ + level = 1, + endLevel, + contentType = "letter", // "letter" or "syllable" + contentCount = 10, + isShowcase = false, + handleNext, + handleBack, + applyStep, + failRedirect, + passRedirect, + isF3FlowActive, + f3FlowStep, + header = "Letter Speed", + points = 0, + steps = 10, + currentStep = 1, + progressData, + showProgress = true, + background = "#FFB31F", + enableNext, + setEnableNext, + loading, + setOpenMessageDialog, + vocabCount, + wordCount, + showTimer = false, + milestoneLevel, + setProgressData, + setCurrentQuestion, +}) => { + const navigate = useNavigate(); + const lang = getLocalData("lang") || "en"; + const [currentLevel, setCurrentLevel] = useState(level || 1); + const [currentItemIndex, setCurrentItemIndex] = useState(0); + const [score, setScore] = useState(0); + const [timeRemaining, setTimeRemaining] = useState(30); // 30 seconds for Apply steps + const [isPlaying, setIsPlaying] = useState(false); + const [isComplete, setIsComplete] = useState(false); + const [currentItem, setCurrentItem] = useState(null); + const [items, setItems] = useState([]); + const [correctCount, setCorrectCount] = useState(0); + const [wrongCount, setWrongCount] = useState(0); + const timerRef = useRef(null); + const audioRef = useRef(null); + + // Generate items based on contentType + useEffect(() => { + const generateItems = () => { + const generatedItems = []; + + // For letters: A-Z + // For syllables: common syllables like "at", "an", "in", "on", etc. + if (contentType === "letter") { + const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); + for (let i = 0; i < contentCount; i++) { + const letter = letters[Math.floor(Math.random() * letters.length)]; + generatedItems.push({ + id: i, + text: letter, + type: "letter", + }); + } + } else if (contentType === "syllable") { + const syllables = [ + "at", + "an", + "in", + "on", + "am", + "it", + "up", + "en", + "ed", + "ot", + ]; + for (let i = 0; i < contentCount; i++) { + const syllable = + syllables[Math.floor(Math.random() * syllables.length)]; + generatedItems.push({ + id: i, + text: syllable, + type: "syllable", + }); + } + } + + setItems(generatedItems); + if (generatedItems.length > 0) { + setCurrentItem(generatedItems[0]); + } + }; + + generateItems(); + }, [contentType, contentCount, currentLevel]); + + // Timer for Apply steps + useEffect(() => { + if (isShowcase && isPlaying && timeRemaining > 0) { + timerRef.current = setTimeout(() => { + setTimeRemaining((prev) => { + if (prev <= 1) { + handleTimeUp(); + return 0; + } + return prev - 1; + }); + }, 1000); + } + + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, [isShowcase, isPlaying, timeRemaining]); + + const handleTimeUp = () => { + setIsPlaying(false); + // Check pass criteria: correct letters > 10 within 30 seconds + if (correctCount > 10) { + handleLevelPass(); + } else { + handleLevelFail(); + } + }; + + const handleStart = () => { + setIsPlaying(true); + setTimeRemaining(100); + setCorrectCount(0); + setWrongCount(0); + setCurrentItemIndex(0); + if (items.length > 0) { + setCurrentItem(items[0]); + } + }; + + const handleCorrect = () => { + playSound(correctSound); + setCorrectCount((prev) => prev + 1); + setScore((prev) => prev + 1); + moveToNextItem(); + }; + + const handleWrong = () => { + playSound(wrongSound); + setWrongCount((prev) => prev + 1); + moveToNextItem(); + }; + + const moveToNextItem = () => { + if (currentItemIndex < items.length - 1) { + setCurrentItemIndex((prev) => { + const nextIndex = prev + 1; + setCurrentItem(items[nextIndex]); + return nextIndex; + }); + } else { + // Completed all items in this level + if (isShowcase) { + // For Apply steps, check if we need to move to next level + if (currentLevel < (endLevel || 1)) { + // Move to next level + setCurrentLevel((prev) => prev + 1); + setCurrentItemIndex(0); + setCorrectCount(0); + setWrongCount(0); + // Regenerate items for new level + } else { + // All levels completed + handleLevelPass(); + } + } else { + // Practice step completed + handleStepComplete(); + } + } + }; + + const handleLevelPass = () => { + setIsPlaying(false); + setIsComplete(true); + + // For Apply steps, check if all levels are passed + if (isShowcase && currentLevel >= (endLevel || 1)) { + // All levels passed - will redirect via onLevelComplete callback + setTimeout(() => { + if (handleNext) { + handleNext(); + } + }, 2000); + } else if (isShowcase) { + // Move to next level + setCurrentLevel((prev) => prev + 1); + setCurrentItemIndex(0); + setCorrectCount(0); + setWrongCount(0); + setIsComplete(false); + setIsPlaying(false); + } + }; + + const handleLevelFail = () => { + setIsPlaying(false); + // Redirect to failRedirect + if (failRedirect && handleNext) { + setTimeout(() => { + handleNext(); + }, 2000); + } + }; + + const handleStepComplete = () => { + setIsComplete(true); + setTimeout(() => { + if (handleNext) { + handleNext(); + } + }, 2000); + }; + + const playSound = (soundFile) => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + audioRef.current = new Audio(soundFile); + audioRef.current.play().catch((err) => { + console.log("Audio play error:", err); + }); + }; + + const handleRetry = () => { + setCurrentItemIndex(0); + setCorrectCount(0); + setWrongCount(0); + setScore(0); + setIsComplete(false); + setIsPlaying(false); + setTimeRemaining(100); + if (items.length > 0) { + setCurrentItem(items[0]); + } + }; + + if (!currentItem && items.length === 0) { + return ( + {}} + level={milestoneLevel || "B"} + flowNames={[]} + activeFlow={isF3FlowActive ? `P${f3FlowStep?.step?.step || 1}` : ""} + progressData={progressData} + showProgress={showProgress} + points={points} + vocabCount={vocabCount} + wordCount={wordCount} + > + + + + + ); + } + + return ( + {}} + level={milestoneLevel || "B"} + flowNames={[]} + activeFlow={isF3FlowActive ? `P${f3FlowStep?.step?.step || 1}` : ""} + progressData={progressData} + showProgress={showProgress} + points={points} + vocabCount={vocabCount} + wordCount={wordCount} + > + + {isShowcase && ( + + + Level {currentLevel} / {endLevel || 1} + + {isPlaying && ( + Time: {timeRemaining}s + )} + + Correct: {correctCount} | Wrong: {wrongCount} + + + )} + + {!isPlaying && !isComplete && ( + + + {isShowcase ? `Level ${currentLevel}` : "Letter Speed"} + + + {isShowcase + ? "Identify as many letters as possible in 30 seconds!" + : "Click Start to begin identifying letters"} + + + + )} + + {isPlaying && currentItem && ( + + + {currentItem.text} + + + + + + + Item {currentItemIndex + 1} / {items.length} + + + )} + + {isComplete && ( + + + {isShowcase && correctCount > 10 + ? "Level Passed!" + : "Step Completed!"} + + + Score: {correctCount} correct, {wrongCount} wrong + + {isShowcase && correctCount <= 10 && ( + + )} + {isShowcase && + correctCount > 10 && + currentLevel < (endLevel || 1) && ( + + )} + + )} + + + ); +}; + +export default LetterLauncher; diff --git a/src/components/Practice/LetterLauncherMechanics.jsx b/src/components/Practice/LetterLauncherMechanics.jsx new file mode 100644 index 00000000..a6e8f040 --- /dev/null +++ b/src/components/Practice/LetterLauncherMechanics.jsx @@ -0,0 +1,2419 @@ +import React, { useState, useEffect, useCallback, useRef } from "react"; +import { useNavigate } from "react-router-dom"; +import MainLayout from "../Layouts.jsx/MainLayout"; +import { + getLocalData, + setLocalData, + practiceSteps, + levelGetContent, +} from "../../utils/constants"; +import { + addLesson, + addPointer, +} from "../../services/orchestration/orchestrationService"; +import { getF3FlowStep, advanceF3Flow, F3_FLOW } from "../../RFlow/F3"; + +// Import from library +import { + LetterLauncherGameCore, + LanguageProvider, + AudioLanguageProvider, + sessionManager, + sessionTelemetryManager, + SpaceBackground, + playLetterAudio, + FuelProgressBar, + getFuelRequirement, + getMissionDestination, + calculateFuel, + TryAgain, + SuccessScreen, + memoryGameDataLoader, +} from "../../lib/axl-explorations/src/lib/index"; +import { trackingAssessmentService } from "../../lib/axl-explorations/src/utils/trackingAssessmentService"; +// Import preview components directly +import { CountdownTimer } from "../../lib/axl-explorations/src/components/CountdownTimer"; +import { LetterLauncherGameStoryPreview } from "../../lib/axl-explorations/src/components/games/LetterLauncherGameStoryPreview"; +import { Button } from "../../lib/axl-explorations/src/components/ui/button"; +import { ArrowLeft } from "lucide-react"; +/** + * Wrapper component that integrates axl-explorations ROARRapidVisualGameCore + * into the Practice.jsx mechanics system for F3 flow Letter Launcher + */ +const LetterLauncherMechanicsContent = ({ + page, + setPage, + level, // Starting level (1, 2, 3, etc.) + header, + points, + steps, + currentStep, + progressData, + showProgress, + background, + handleNext, + handleBack, + enableNext, + setEnableNext, + isShowCase, + loading, + setOpenMessageDialog, + vocabCount, + wordCount, + showTimer, + milestoneLevel, + endLevel, // Optional: end level for level range + startShowCase, + setStartShowCase, + setProgressData, + setCurrentQuestion, + applyStep, + failRedirect, + passRedirect, + isF3FlowActive, + f3FlowStep, + contentType = "letter", // "letter" or "syllable" + contentCount = 10, + sessionId, // Optional: Session ID from parent + confidentLetters, // Optional: Letters user is confident with (appear less frequently) +}) => { + const [currentGameLevel, setCurrentGameLevel] = useState(level || 1); + const [isGameComplete, setIsGameComplete] = useState(false); + const [sessionInitialized, setSessionInitialized] = useState(false); + // Track step start time for duration calculation + const [stepStartTime, setStepStartTime] = useState(null); + const navigate = useNavigate(); + + // Preview states - only show preview for P1 step + const isP1Step = + isF3FlowActive && + f3FlowStep?.step?.type === "P" && + f3FlowStep?.step?.step === 1; + const [showPreview, setShowPreview] = useState(isP1Step); // Show preview/countdown when first opening game (only for P1) + const [showStoryPreview, setShowStoryPreview] = useState(false); // Show story preview after countdown + const [level19HasProgress, setLevel19HasProgress] = useState(false); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + + // Reset currentGameLevel when level prop changes (e.g., when step changes) + useEffect(() => { + if (level && level !== currentGameLevel) { + // Don't reset if we're in an Apply step with multiple levels and: + // 1. We've completed all levels (currentGameLevel >= endLevel), OR + // 2. We're advancing levels (currentGameLevel > level) + // This prevents resetting when advancing through levels or when moving to Memory Challenge + if (isShowCase === true && endLevel) { + if (currentGameLevel >= endLevel) { + // All levels complete, don't reset - let handleNext handle the transition + return; + } + if (currentGameLevel > level) { + // We're advancing levels (e.g., level 20 > starting level 19), don't reset + return; + } + } + setCurrentGameLevel(level); + // Reset preview states when level changes (preview only shows for P1) + const isCurrentlyP1 = + isF3FlowActive && + f3FlowStep?.step?.type === "P" && + f3FlowStep?.step?.step === 1; + if (!isCurrentlyP1) { + setShowPreview(false); + setShowStoryPreview(false); + } else { + setShowPreview(true); + } + } + }, [ + level, + currentGameLevel, + isF3FlowActive, + f3FlowStep, + isShowCase, + endLevel, + ]); + + // Update preview state when F3 flow step changes + useEffect(() => { + const isCurrentlyP1 = + isF3FlowActive && + f3FlowStep?.step?.type === "P" && + f3FlowStep?.step?.step === 1; + if (isCurrentlyP1) { + setShowPreview(true); + setShowStoryPreview(false); + } else { + setShowPreview(false); + setShowStoryPreview(false); + } + }, [isF3FlowActive, f3FlowStep]); + + // Ensure isShowCase is a boolean (handle undefined case) + const effectiveIsShowCase = isShowCase === true; + + // For Apply steps, initialize startShowCase to false if not provided + // This ensures the start screen shows before the game begins + const [localStartShowCase, setLocalStartShowCase] = useState( + startShowCase !== undefined + ? startShowCase + : effectiveIsShowCase + ? false + : true + ); + + // Use prop if provided, otherwise use local state + const effectiveStartShowCase = + startShowCase !== undefined ? startShowCase : localStartShowCase; + const effectiveSetStartShowCase = setStartShowCase || setLocalStartShowCase; + + const lang = getLocalData("lang") || "en"; + + // Map language to library Language type + const initialLanguage = + lang === "en" + ? "en" + : lang === "te" + ? "te" + : lang === "kn" + ? "kn" + : lang === "mr" + ? "mr" + : lang === "hi" + ? "hi" + : "en"; + const initialAudioLanguage = initialLanguage; + + // Game key for telemetry (matches LetterLauncherGame format) + const gameKey = initialLanguage + ? `letterLauncher_${initialLanguage}` + : "letterLauncher"; + useEffect(() => { + localStorage.setItem("selectedLanguage", initialLanguage); + localStorage.setItem("selectedAudioLanguage", initialAudioLanguage); + }, [initialLanguage, initialAudioLanguage]); + + // Initialize telemetry session before game starts + useEffect(() => { + const initializeSession = async () => { + try { + const currentUser = sessionManager.getCurrentUser(); + let userId = "anonymous"; + + if (currentUser && currentUser.username) { + userId = currentUser.username; + } else { + const storedUser = + localStorage.getItem("user") || localStorage.getItem("username"); + if (storedUser) { + userId = storedUser; + } + } + + const currentSession = sessionTelemetryManager.getCurrentSession(); + if (!currentSession || !currentSession.isActive) { + await sessionTelemetryManager.startUserSession(userId); + console.log( + "✅ Telemetry session initialized for Letter Launcher game" + ); + } + + setSessionInitialized(true); + } catch (error) { + console.warn("Failed to initialize telemetry session:", error); + + setSessionInitialized(true); + } + }; + + const timeoutId = setTimeout(() => { + console.warn("Session initialization timeout - proceeding anyway"); + + setSessionInitialized(true); + }, 3000); + + document.body.classList.add("letter-launcher-active"); + + initializeSession(); + + return () => { + document.body.classList.remove("letter-launcher-active"); + clearTimeout(timeoutId); + }; + }, []); + + const handleGameBack = async () => { + // End telemetry subsession with back button (matches LetterGame pattern) + try { + await sessionTelemetryManager.endSubSessionWithBackButton(); + console.log( + "✅ Letter Launcher telemetry subsession ended (back button)" + ); + } catch (error) { + console.error( + "Error ending Letter Launcher telemetry subsession (back button):", + error + ); + } + + if (handleBack) { + handleBack(); + } + }; + + // Helper function to map redirect string (e.g., "P1", "P6") to F3 flow index + // F3_FLOW: P1(0), P2(1), P3(2), P4(3), P5(4), A1(5), P6(6), P7(7), P8(8), P9(9), P10(10), A2(11) + const getF3FlowIndexFromRedirect = (redirect) => { + if (!redirect || typeof redirect !== "string") return null; + + // Match "P" followed by a number (e.g., "P1", "P6") + const match = redirect.match(/^P(\d+)$/); + if (match) { + const practiceNum = parseInt(match[1], 10); + // F3_FLOW indices: P1=0, P2=1, P3=2, P4=3, P5=4, A1=5, P6=6, P7=7, P8=8, P9=9, P10=10, A2=11 + // So P1-P5 map to 0-4, P6-P10 map to 6-10 + if (practiceNum >= 1 && practiceNum <= 5) { + return practiceNum - 1; // P1=0, P2=1, P3=2, P4=3, P5=4 + } else if (practiceNum >= 6 && practiceNum <= 10) { + return practiceNum; // P6=6, P7=7, P8=8, P9=9, P10=10 + } + } + + return null; + }; + + // Generate questions based on contentType and language + const generateQuestions = () => { + const questions = []; + let letters = []; + // Ensure only supported languages are passed + const supportedLanguage = + initialLanguage === "te" || + initialLanguage === "mr" || + initialLanguage === "kn" || + initialLanguage === "hi" + ? initialLanguage + : "en"; + + if (contentType === "letter" || contentType === "syllable") { + // For Telugu, Kannada, and Marathi, use exact level mapping + if ( + supportedLanguage === "te" || + supportedLanguage === "kn" || + supportedLanguage === "mr" || + supportedLanguage === "en" || + supportedLanguage === "hi" + ) { + const levelKey = currentGameLevel.toString(); + letters = memoryGameDataLoader.getLettersByLevel( + supportedLanguage, + levelKey + ); + + console.log( + `Letter Launcher - Using ${supportedLanguage} letters for level ${currentGameLevel}:`, + { + lettersCount: letters.length, + sampleLetters: letters.slice(0, 10), + } + ); + } + } + + // Fallback to English if no letters found (for non-English languages only) + if (letters.length === 0 && initialLanguage !== "en") { + console.warn( + "Letter Launcher - No letters found for non-English language, falling back to English" + ); + letters = + contentType === "letter" + ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("") + : ["at", "an", "in", "on", "am", "it", "up", "en", "ed", "ot"]; + } + + // Build weighted letter array based on confidentLetters + const buildWeightedLetterArray = (letters) => { + if ( + !confidentLetters || + confidentLetters.length === 0 || + letters.length === 0 + ) { + return letters; + } + + // Normalize confident letters to uppercase + const normalizedConfident = confidentLetters + .map((letter) => + typeof letter === "string" ? letter.toUpperCase() : "" + ) + .filter(Boolean); + + // Separate letters into confident and non-confident + const confident = []; + const nonConfident = []; + + letters.forEach((letter) => { + const upperLetter = letter.toUpperCase(); + if (normalizedConfident.includes(upperLetter)) { + confident.push(letter); + } else { + nonConfident.push(letter); + } + }); + + // Build weighted array: + // - Confident letters: appear 1 time (reduced frequency) + // - Non-confident letters: appear 3 times (increased frequency for practice) + const weightedArray = []; + + // Add confident letters once + confident.forEach((letter) => { + weightedArray.push(letter); + }); + + // Add non-confident letters multiple times (3x for more practice) + for (let i = 0; i < 3; i++) { + nonConfident.forEach((letter) => { + weightedArray.push(letter); + }); + } + + console.log("Letter Launcher - Weighted array with confident letters:", { + totalLetters: letters.length, + confidentLetters: confident, + nonConfidentLetters: nonConfident, + weightedArrayLength: weightedArray.length, + confidentCount: confident.length, + nonConfidentCount: nonConfident.length * 3, + }); + + return weightedArray.length > 0 ? weightedArray : letters; + }; + + // Apply weighted selection + const weightedLetters = buildWeightedLetterArray(letters); + + console.log("Letter Launcher - Generating questions:", { + contentCount, + contentType, + language: initialLanguage, + level: currentGameLevel, + availableLetters: letters.length, + weightedLettersCount: weightedLetters.length, + expectedQuestions: contentCount, + }); + const matchesCount = Math.floor(contentCount / 2); + const mismatchesCount = contentCount - matchesCount; + + // Create exact 50:50 match distribution + const matchArray = [ + ...Array(matchesCount).fill(true), + ...Array(mismatchesCount).fill(false), + ]; + + // Shuffle once + matchArray.sort(() => Math.random() - 0.5); + + for (let i = 0; i < contentCount; i++) { + // Select from weighted array instead of original letters array + const audioLetter = + weightedLetters[Math.floor(Math.random() * weightedLetters.length)]; + // Randomly decide if displayed letter matches audio (50% match, 50% mismatch) + const isMatch = matchArray[i]; + const displayedLetter = isMatch + ? audioLetter + : weightedLetters.filter((l) => l !== audioLetter)[ + Math.floor(Math.random() * (weightedLetters.length - 1)) + ]; + const question = { + audioLetter, + displayedLetter, + isMatch, + complexity: "simple", + language: initialLanguage, + }; + // Verify isMatch is correct + const actualMatch = audioLetter === displayedLetter; + if (isMatch !== actualMatch) { + // Question generation mismatch detected - this should not happen + } + + questions.push(question); + } + + console.log("Letter Launcher - Generated questions:", { + generatedCount: questions.length, + expectedCount: contentCount, + match: questions.length === contentCount, + language: initialLanguage, + contentType, + sampleQuestions: questions.slice(0, 3).map((q) => ({ + audioLetter: q.audioLetter, + displayedLetter: q.displayedLetter, + isMatch: q.isMatch, + })), + }); + + return questions; + }; + + const [questions, setQuestions] = useState([]); + // Ref to track questions array changes + const questionsArrayIdRef = useRef(null); + // Ref to track when currentQuestion changes + const previousQuestionRef = useRef(null); + // Ref to track the config that was used to generate current questions + const questionsConfigRef = useRef(null); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [timeRemaining, setTimeRemaining] = useState(100); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [showLetter, setShowLetter] = useState(false); + const [isPlayingAudio, setIsPlayingAudio] = useState(false); + // Ref to prevent double audio playback (React StrictMode double-invocation) + const isPlayingAudioRef = useRef(false); + const currentQuestionAudioKeyRef = useRef(null); + // Ref to store the exact question that was displayed, for validation + const displayedQuestionRef = useRef(null); + // Ref to track last logged question to avoid duplicate render logs + const lastLoggedQuestionRef = useRef(null); + // Ref to lock the question that audio is currently playing for (prevents questions array changes from affecting audio/display) + const lockedQuestionForAudioRef = useRef(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [correctCount, setCorrectCount] = useState(0); + const [wrongCount, setWrongCount] = useState(0); + const [questionStartTime, setQuestionStartTime] = useState(null); + const [currentFuel, setCurrentFuel] = useState(0); + const [fuelEarned, setFuelEarned] = useState(null); + const [questionSummaries, setQuestionSummaries] = useState([]); + const [levelStartTime, setLevelStartTime] = useState(null); + const [totalTimeSpent, setTotalTimeSpent] = useState(0); + + // Get F3 flow assessment parameters from config + const getF3AssessmentParams = () => { + if (!isF3FlowActive || !f3FlowStep?.step) { + return { + sub_session_id: undefined, + sub_milestone_level: undefined, + apply_level: undefined, + sub_apply_level: undefined, + }; + } + + // Get sub_session_id from telemetry + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sub_session_id = currentSubSession?.subSessionId; + + // For F3 flow, sub_milestone_level is always "F3" + const sub_milestone_level = "F3"; + + // Get step title from F3 config + const lang = getLocalData("lang") || "en"; + const f3Config = levelGetContent[lang]?.["F3"]; + const f3StepConfig = + f3Config && Array.isArray(f3Config) && f3Config[f3FlowStep.index] + ? f3Config[f3FlowStep.index] + : null; + const stepTitle = + f3StepConfig?.title || + (f3FlowStep.step?.type === "A" + ? `A${f3FlowStep.step?.step}` + : f3FlowStep.step?.type === "P" + ? `P${f3FlowStep.step?.step}` + : null); + + // Determine apply_level - use step title for all F3 flow steps + const apply_level = stepTitle || undefined; + + return { + sub_session_id, + sub_milestone_level, + apply_level, + // sub_apply_level will be set dynamically based on currentGameLevel + }; + }; + + // Reset step start time when F3 step changes + useEffect(() => { + if (isF3FlowActive && f3FlowStep?.index !== undefined) { + setStepStartTime(Date.now()); + } + }, [f3FlowStep?.index, isF3FlowActive]); + + // Helper function to get F3 step title + const getF3StepTitle = (flowIndex) => { + const lang = getLocalData("lang") || "en"; + const f3Config = levelGetContent[lang]?.["F3"]; + const stepConfig = f3Config?.[flowIndex]; + if (stepConfig?.title) { + return stepConfig.title; + } + // Fallback: construct from F3_FLOW + const flowStep = F3_FLOW[flowIndex]; + if (flowStep) { + return `${flowStep.type}${flowStep.step}`; + } + return undefined; + }; + + // Helper function to calculate duration in seconds + const calculateF3Duration = () => { + if (!stepStartTime) return undefined; + return Math.round((Date.now() - stepStartTime) / 1000); // Duration in seconds + }; + + const assessmentParams = getF3AssessmentParams(); + + // Check if P1 step has progress (for preview display) + useEffect(() => { + const checkP1Progress = async () => { + if (!initialLanguage) { + setIsLoadingLevel(false); + return; + } + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + // For P1 step, we can check progress if needed in the future + setLevel19HasProgress(false); + } catch (error) { + console.error("Error checking P1 step progress:", error); + setLevel19HasProgress(false); + } finally { + setIsLoadingLevel(false); + } + }; + + // Only check progress if it's P1 step + if (isP1Step) { + checkP1Progress(); + } else { + setIsLoadingLevel(false); + } + }, [initialLanguage, gameKey, isP1Step]); + + // Preview handlers + const handleCountdownComplete = () => { + console.log( + "[LetterLauncherMechanics] Countdown complete, showing story preview" + ); + // After countdown, show story preview + setShowStoryPreview(true); + // Also set showPreview to false to prevent countdown from showing again + setShowPreview(false); + }; + + const handleStoryPreviewComplete = () => { + setShowStoryPreview(false); + // After story preview, start the game + setShowPreview(false); + // Reset audio refs so the first question's audio plays fresh after the demo + isPlayingAudioRef.current = false; + currentQuestionAudioKeyRef.current = null; + lockedQuestionForAudioRef.current = null; + }; + + // Reset completion state when step changes (detected by f3FlowStep change) + useEffect(() => { + if (isF3FlowActive && f3FlowStep?.step) { + setIsGameComplete(false); + setLevelFailed(false); + setCurrentQuestionIndex(0); + setCorrectCount(0); + setWrongCount(0); + setCurrentFuel(0); + setShowFeedback(false); + setSelectedAnswer(null); + setShowLetter(false); + setFuelEarned(null); + setQuestionStartTime(null); + // Reset to the starting level for the new step + if (level) { + setCurrentGameLevel(level); + } + if (sessionInitialized) { + // Check if we need to regenerate questions (config changed or questions don't exist) + // Use 'level' prop directly (not currentGameLevel state) since we're about to set it + const levelToUse = level || currentGameLevel; + const currentConfig = `${f3FlowStep?.step?.step}-${f3FlowStep?.step?.type}-${contentType}-${contentCount}-${levelToUse}`; + const configChanged = questionsConfigRef.current !== currentConfig; + const questionsEmpty = questions.length === 0; + + // Prevent question regeneration if audio is currently playing (lock protection) + if (isPlayingAudioRef.current && lockedQuestionForAudioRef.current) { + } else if (configChanged || questionsEmpty) { + const newQuestions = generateQuestions(); + const newArrayId = `${Date.now()}-${newQuestions + .map((q, i) => `${i}:${q.audioLetter}-${q.displayedLetter}`) + .join("|")}`; + + questionsArrayIdRef.current = newArrayId; + questionsConfigRef.current = currentConfig; + setQuestions(newQuestions); + } + } + } + }, [ + isF3FlowActive, + f3FlowStep?.step?.step, + f3FlowStep?.step?.type, + contentType, + contentCount, + sessionInitialized, + ]); + + useEffect(() => { + const initializeGameSession = async () => { + if (sessionInitialized) { + // Check if we need to regenerate questions (config changed or questions don't exist) + const currentConfig = `${f3FlowStep?.step?.step}-${f3FlowStep?.step?.type}-${contentType}-${contentCount}-${currentGameLevel}`; + const configChanged = questionsConfigRef.current !== currentConfig; + const questionsEmpty = questions.length === 0; + + // Prevent question regeneration if audio is currently playing (lock protection) + if (isPlayingAudioRef.current && lockedQuestionForAudioRef.current) { + } else if (configChanged || questionsEmpty) { + const generatedQuestions = generateQuestions(); + const newArrayId = `${Date.now()}-${generatedQuestions + .map((q, i) => `${i}:${q.audioLetter}-${q.displayedLetter}`) + .join("|")}`; + + questionsArrayIdRef.current = newArrayId; + questionsConfigRef.current = currentConfig; + setQuestions(generatedQuestions); + } else { + } + + // Initialize level start time and reset question summaries (always do this) + const now = Date.now(); + setLevelStartTime(now); + setQuestionSummaries([]); + setTotalTimeSpent(0); + + // Start telemetry subsession when game starts + // This matches the pattern used in LetterGame + try { + const currentSubSession = + sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession( + gameKey, + currentGameLevel, + initialLanguage + ); + console.log("✅ Letter Launcher telemetry subsession started:", { + gameKey, + level: currentGameLevel, + language: initialLanguage, + }); + } catch (error) { + console.error( + "Error starting Letter Launcher telemetry subsession:", + error + ); + } + } + }; + + initializeGameSession(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sessionInitialized, contentType, contentCount, currentGameLevel]); + + // Play audio and show letter after audio ends + // For Apply steps, don't start until startShowCase is true + useEffect(() => { + if ( + !sessionInitialized || + questions.length === 0 || + showFeedback || + isGameComplete + ) { + return; + } + + // For P1 step: wait for preview/demo (countdown + story preview) to complete before playing audio + if (showPreview || showStoryPreview) { + return; + } + + // For Apply steps, wait for start screen to be dismissed + if (effectiveIsShowCase && !effectiveStartShowCase) { + return; + } + + const currentQuestion = questions[currentQuestionIndex]; + + if (!currentQuestion) { + return; + } + + // Create a unique key for this question's audio + const audioKey = `${currentQuestionIndex}-${currentQuestion.audioLetter}-${currentQuestion.displayedLetter}`; + + // Prevent double audio playback: atomic check-and-set pattern + // If already playing the same audio, skip immediately + if ( + isPlayingAudioRef.current && + currentQuestionAudioKeyRef.current === audioKey + ) { + return; + } + + // Atomic check-and-set: if ref is already true for a different audio, skip + // This handles the case where we're transitioning between questions + if ( + isPlayingAudioRef.current && + currentQuestionAudioKeyRef.current !== audioKey + ) { + return; + } + + // Set refs SYNCHRONOUSLY and IMMEDIATELY before any async operations + // This prevents React StrictMode double-invocation from both passing the check + isPlayingAudioRef.current = true; + currentQuestionAudioKeyRef.current = audioKey; + // Lock the question that audio is playing for - prevents questions array changes from affecting this audio/display + lockedQuestionForAudioRef.current = { + ...currentQuestion, + questionIndex: currentQuestionIndex, + }; + + const playQuestionAudio = async () => { + setShowLetter(false); + setIsPlayingAudio(true); + + // Play audio + + try { + await playLetterAudio(currentQuestion.audioLetter, initialLanguage); + } catch (error) { + // If audio fails, reset refs so we don't get stuck + isPlayingAudioRef.current = false; + currentQuestionAudioKeyRef.current = null; + lockedQuestionForAudioRef.current = null; + setIsPlayingAudio(false); + + return; // Exit early if audio fails + } + + // After audio ends, show the letter + setShowLetter(true); + setIsPlayingAudio(false); + setQuestionStartTime(Date.now()); + + // CRITICAL: Use the question from the ref (what was actually rendered) instead of re-reading from array + // The ref is set during render and represents what's actually displayed on screen + const refQuestion = displayedQuestionRef.current; + const latestQuestion = questions[currentQuestionIndex]; + + // Determine which question to use - prefer ref (what was rendered) over closure or array + let questionToUse; + if (refQuestion && refQuestion.questionIndex === currentQuestionIndex) { + // Use ref question if it exists and matches current index (this is what was actually displayed) + questionToUse = refQuestion; + } else { + // Fallback: use latest from array, or closure question + questionToUse = latestQuestion || currentQuestion; + // Update ref with the question we're using (for validation) + displayedQuestionRef.current = { + ...questionToUse, + questionIndex: currentQuestionIndex, + }; + } + + // Show what will happen for each option BEFORE user selects + const correctAnswer = questionToUse.isMatch; // true = TICK, false = CROSS + const ifSelectTick = true === questionToUse.isMatch; + const ifSelectCross = false === questionToUse.isMatch; + + // Clear refs after audio completes + isPlayingAudioRef.current = false; + // Clear the lock after audio finishes and letter is shown + lockedQuestionForAudioRef.current = null; + }; + + playQuestionAudio(); + + // Cleanup function: if component unmounts or effect re-runs before audio completes, + // we don't reset refs here (they'll be reset after audio completes or in handleContinue) + // This prevents race conditions with React StrictMode double-invocation + return () => { + // Only cleanup if this is a true unmount (not just a re-run) + // We can't easily detect this, so we rely on the ref check at the start of the effect + }; + }, [ + sessionInitialized, + questions, + currentQuestionIndex, + showFeedback, + isGameComplete, + showPreview, + showStoryPreview, + effectiveIsShowCase, + effectiveStartShowCase, + initialLanguage, + ]); + + // Track when currentQuestion changes + useEffect(() => { + const currentQuestion = questions[currentQuestionIndex]; + const questionKey = currentQuestion + ? `${currentQuestionIndex}-${currentQuestion.audioLetter}-${currentQuestion.displayedLetter}` + : null; + if (previousQuestionRef.current !== questionKey) { + const previousKey = previousQuestionRef.current; + previousQuestionRef.current = questionKey; + } + }, [questions, currentQuestionIndex]); + + // Note: We don't reset refs on currentQuestionIndex change here to avoid race conditions + // Refs are reset in handleContinue (when user moves to next question) and after audio completes + + // Start timer when start screen is dismissed for Apply steps + useEffect(() => { + if ( + effectiveIsShowCase && + effectiveStartShowCase && + sessionInitialized && + questions.length > 0 && + !isGameComplete && + !isTimerRunning + ) { + setIsTimerRunning(true); + setTimeRemaining(100); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + effectiveIsShowCase, + effectiveStartShowCase, + sessionInitialized, + questions.length, + isGameComplete, + ]); + + // Handle timer expiration - wrapped in useCallback to prevent dependency issues + const handleTimeUp = useCallback(() => { + setIsTimerRunning(false); + + // IMPORTANT: Don't complete the game immediately when timer expires + // For Apply steps with timer: stop showing new questions, but allow current question to finish + // The game will complete in handleAnswerSelect after the current question is answered + // This ensures all questions that were started can be completed + + console.log( + "Letter Launcher - Timer expired, stopping timer but allowing current question to complete:", + { + currentQuestionIndex, + questionsLength: questions.length, + totalQuestions: contentCount, + } + ); + }, [currentQuestionIndex, questions.length, contentCount]); + + // Timer countdown for Apply steps + useEffect(() => { + if ( + effectiveIsShowCase && + effectiveStartShowCase && + isTimerRunning && + timeRemaining > 0 + ) { + const timer = setTimeout(() => { + setTimeRemaining((prev) => { + if (prev <= 1) { + handleTimeUp(); + return 0; + } + return prev - 1; + }); + }, 1000); + return () => clearTimeout(timer); + } + }, [ + effectiveIsShowCase, + effectiveStartShowCase, + handleTimeUp, + isTimerRunning, + timeRemaining, + ]); + + const handleAnswerSelect = async (isMatch) => { + if (showFeedback || isPlayingAudio || !showLetter) return; + + // CRITICAL: Use the question from the ref (what was actually displayed) instead of reading from array + // This ensures we validate against the exact question that was shown, even if questions array changed + const displayedQuestion = displayedQuestionRef.current; + const arrayQuestion = questions[currentQuestionIndex]; + + // Determine which question to use for validation + let currentQuestion; + if ( + displayedQuestion && + displayedQuestion.questionIndex === currentQuestionIndex + ) { + // Use ref question if it exists and matches current index + currentQuestion = displayedQuestion; + } else { + // Fallback to array question + currentQuestion = arrayQuestion; + } + + if (!currentQuestion) { + return; + } + + const isCorrectAnswer = isMatch === currentQuestion.isMatch; + + // Calculate fuel based on response time + let fuelResult = null; + let updatedFuel = currentFuel; // Track updated fuel to pass to handlers + if (questionStartTime) { + const responseTime = Date.now() - questionStartTime; + fuelResult = calculateFuel(responseTime, isCorrectAnswer); + if (isCorrectAnswer) { + // Calculate updated fuel total including current question + updatedFuel = currentFuel + fuelResult.fuelEarned; + setCurrentFuel((prev) => prev + fuelResult.fuelEarned); + } + } + + setSelectedAnswer(isMatch); + setShowFeedback(true); + setIsCorrect(isCorrectAnswer); + setFuelEarned(fuelResult); + + // Track question for assessment + const responseTime = questionStartTime ? Date.now() - questionStartTime : 0; + + // Send telemetry ASSESS event (matches LetterLauncherGame format) + // Format user answer as colon-separated string: "audioLetter:displayedLetter:userSelected" + // Example: "A:A:true" (user selected match) or "A:B:false" (user selected non-match) + const userAnswer = `${currentQuestion.audioLetter}:${ + currentQuestion.displayedLetter + }:${isMatch ? "true" : "false"}`; + + // Format correct answer as colon-separated string: "audioLetter:displayedLetter:isMatch" + // Example: "A:A:true" (is a match) or "A:B:false" (is not a match) + const correctAnswer = `${currentQuestion.audioLetter}:${ + currentQuestion.displayedLetter + }:${currentQuestion.isMatch ? "true" : "false"}`; + + const questionId = `letterLauncher_${currentGameLevel}_${currentQuestionIndex}`; + + try { + await sessionTelemetryManager.sendAssessEvent( + questionId, + "letterLauncher", + userAnswer, + correctAnswer, + isCorrectAnswer, + responseTime + ); + // Update subsession with correct/incorrect + sessionTelemetryManager.updateSubSession(isCorrectAnswer); + console.log("✅ Letter Launcher telemetry assess event sent:", { + questionId, + isCorrect: isCorrectAnswer, + responseTime, + }); + } catch (error) { + console.error( + "Error sending Letter Launcher telemetry assess event:", + error + ); + } + + const questionSummary = { + questionId: questionId, + questionType: "letterLauncher", // Required by QuestionSummary interface + userAnswer: userAnswer, // Use colon-separated format for consistency + correctAnswer: correctAnswer, // Use colon-separated format for consistency + isCorrect: isCorrectAnswer, + responseTime: responseTime, + complexity: currentQuestion.complexity || "simple", + }; + // CRITICAL: Use functional update to ensure we always have the latest state + // This prevents race conditions when questions are answered quickly + let updatedSummaries; + setQuestionSummaries((prev) => { + updatedSummaries = [...prev, questionSummary]; + return updatedSummaries; + }); + + if (isCorrectAnswer) { + setCorrectCount((prev) => prev + 1); + } else { + setWrongCount((prev) => prev + 1); + } + + // Move to next question after feedback + // DO NOT update progress here - only update when ALL questions are complete + setTimeout(() => { + // Check if timer expired and we should stop showing new questions + const timerExpired = !isTimerRunning && effectiveIsShowCase; + + // Check if all questions have been answered + // When we answer question at index N, we've answered N+1 questions total + // IMPORTANT: Use contentCount to determine completion, not questions.length + // This ensures we complete after answering all contentCount questions + const questionsAnswered = currentQuestionIndex + 1; // +1 because we just answered this question + const allQuestionsAnswered = questionsAnswered >= contentCount; + + // Debug: Verify question count matches contentCount + if (questions.length !== contentCount) { + console.warn("Letter Launcher - Question count mismatch:", { + questionsLength: questions.length, + contentCount, + expected: contentCount, + actual: questions.length, + }); + } + + console.log("Letter Launcher - After answer, checking completion:", { + currentQuestionIndex, + questionsLength: questions.length, + totalQuestions: contentCount, + questionsAnswered, + allQuestionsAnswered, + timerExpired, + isTimerRunning, + effectiveIsShowCase, + }); + + // IMPORTANT: Always complete if all questions are answered, regardless of timer + if (allQuestionsAnswered) { + // All questions answered - complete the game + console.log( + "Letter Launcher - All questions answered, completing game:", + { + currentQuestionIndex, + questionsLength: questions.length, + totalQuestions: contentCount, + questionsAnswered, + allQuestionsAnswered, + } + ); + + // Check pass criteria before deciding what to do + // CRITICAL: Use updatedFuel (includes last question) instead of currentFuel (stale state) + const { requiredFuel } = getFuelRequirement( + currentGameLevel, + contentCount + ); + const hasEnoughFuel = updatedFuel >= requiredFuel; + const minCorrectThreshold = Math.max(7, Math.floor(contentCount * 0.7)); + const hasEnoughCorrect = correctCount >= minCorrectThreshold; + + // Debug logging + console.log("Letter Launcher - Pass/Fail Check:", { + correctCount, + contentCount, + minCorrectThreshold, + hasEnoughCorrect, + currentFuel, + updatedFuel, + requiredFuel, + hasEnoughFuel, + isShowCase, + effectiveIsShowCase, + isShowCaseType: typeof isShowCase, + willUseFuelCheck: effectiveIsShowCase, + }); + + // For Apply steps (effectiveIsShowCase), pass if user has enough fuel + // For Practice steps (!effectiveIsShowCase), pass if user has enough correct answers + if (effectiveIsShowCase) { + // For Apply steps: pass if user has enough fuel (fuel is the primary metric) + // Fuel already accounts for both speed and accuracy + if (hasEnoughFuel) { + // User passed - show success screen for this level + // The success screen will handle moving to next level or Memory Challenge + console.log( + `Letter Launcher - Level ${currentGameLevel} passed${ + endLevel && currentGameLevel < endLevel + ? `, more levels to complete` + : `, all levels complete` + }` + ); + handleLevelPass(updatedSummaries, updatedFuel); + } else { + // User failed - show failure screen, will redirect to P1 on "Try Again" + // Level 1/2/3 fail → P1 + console.log( + `Letter Launcher - Level ${currentGameLevel} failed, will redirect to ${ + failRedirect || "P1" + } on Try Again` + ); + handleLevelFail(updatedSummaries, updatedFuel); + } + } else { + // Practice step: pass if user has enough fuel OR enough correct answers + // Fuel is preferred metric, but also allow passing with good accuracy + // Use the same threshold calculated above (70% accuracy, minimum 7) + if (hasEnoughFuel) { + // User passed - show success screen + handleStepComplete(updatedSummaries, updatedFuel); + } else { + // User failed - show failure screen, do NOT advance + handleLevelFail(updatedSummaries, updatedFuel); + } + } + } else if (timerExpired && currentQuestionIndex < questions.length - 1) { + // Timer expired but not all questions answered - complete with current progress + console.log( + "Letter Launcher - Timer expired, completing with current progress:", + { + currentQuestionIndex, + questionsLength: questions.length, + totalQuestions: contentCount, + answeredQuestions: currentQuestionIndex + 1, + } + ); + + // Check pass criteria before deciding what to do + // CRITICAL: Use updatedFuel (includes last question) instead of currentFuel (stale state) + const { requiredFuel } = getFuelRequirement( + currentGameLevel, + contentCount + ); + const hasEnoughFuel = updatedFuel >= requiredFuel; + const minCorrectThreshold = Math.max(7, Math.floor(contentCount * 0.7)); + const hasEnoughCorrect = correctCount >= minCorrectThreshold; + + // Debug logging + console.log("Letter Launcher - Pass/Fail Check (Timer Expired):", { + correctCount, + contentCount, + minCorrectThreshold, + hasEnoughCorrect, + currentFuel, + updatedFuel, + requiredFuel, + hasEnoughFuel, + effectiveIsShowCase, + }); + + // For Apply steps (effectiveIsShowCase), pass if user has enough fuel + // For Practice steps (!effectiveIsShowCase), pass if user has enough correct answers + if (effectiveIsShowCase) { + if (hasEnoughFuel) { + console.log( + `Letter Launcher - Level ${currentGameLevel} passed (timer expired)` + ); + handleLevelPass(updatedSummaries, updatedFuel); + } else { + console.log( + `Letter Launcher - Level ${currentGameLevel} failed (timer expired)` + ); + handleLevelFail(updatedSummaries, updatedFuel); + } + } else { + if (hasEnoughFuel || hasEnoughCorrect) { + handleStepComplete(updatedSummaries, updatedFuel); + } else { + handleLevelFail(updatedSummaries, updatedFuel); + } + } + } else if (currentQuestionIndex < contentCount - 1 && !timerExpired) { + // Move to next question - game is still in progress and timer hasn't expired + // IMPORTANT: Use contentCount to ensure we show all questions + console.log("Letter Launcher - Moving to next question:", { + currentIndex: currentQuestionIndex, + nextIndex: currentQuestionIndex + 1, + totalQuestions: contentCount, + questionsLength: questions.length, + }); + setCurrentQuestionIndex((prev) => prev + 1); + setShowFeedback(false); + setSelectedAnswer(null); + setShowLetter(false); + setFuelEarned(null); + // Clear the displayed question ref when moving to next question + displayedQuestionRef.current = null; + } + }, 2000); + }; + + const handleContinue = () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex((prev) => prev + 1); + setShowFeedback(false); + setSelectedAnswer(null); + setShowLetter(false); + // Reset audio refs when moving to next question + isPlayingAudioRef.current = false; + currentQuestionAudioKeyRef.current = null; + lockedQuestionForAudioRef.current = null; + // Clear the displayed question ref when moving to next question + displayedQuestionRef.current = null; + } + }; + + const [levelFailed, setLevelFailed] = useState(false); + + const handleLevelPass = async (questionSummariesParam, fuelParam) => { + setIsTimerRunning(false); + setIsGameComplete(true); + setLevelFailed(false); + + // CRITICAL: Always use the parameter as it contains the latest question + // The parameter is always passed from handleAnswerSelect to ensure all questions are included + const questionSummaries = questionSummariesParam || []; + // CRITICAL: Use passed fuel parameter (includes last question) instead of stale currentFuel state + const finalFuel = fuelParam !== undefined ? fuelParam : currentFuel; + + // Calculate total time spent + const timeSpent = levelStartTime + ? Math.round((Date.now() - levelStartTime) / 1000) + : 0; + setTotalTimeSpent((prev) => prev + timeSpent); + + // End telemetry subsession and flush events (matches LetterGame pattern) + try { + await sessionTelemetryManager.endSubSession(); + await sessionTelemetryManager.flushAssessEventBatch(); + console.log("✅ Letter Launcher telemetry subsession ended and flushed"); + } catch (error) { + console.error( + "Error ending Letter Launcher telemetry subsession:", + error + ); + } + + // Call assessment API + const currentUser = sessionManager.getCurrentUser(); + if (currentUser && questionSummaries && questionSummaries.length > 0) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + // Use sessionId prop if provided, otherwise fallback to telemetry sessionId + const effectiveSessionId = sessionId || currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + const actualCorrect = questionSummaries.filter((q) => q.isCorrect).length; + const { requiredFuel } = getFuelRequirement( + currentGameLevel, + contentCount + ); + const missionDestination = getMissionDestination(currentGameLevel); + + try { + const assessmentResponse = + await trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: `letterLauncher_${initialLanguage}`, + gameTitle: "Letter Launcher", + level: currentGameLevel, + language: initialLanguage, + totalQuestions: contentCount, + correctAnswers: actualCorrect, + totalScore: finalFuel, + timeSpent: timeSpent, + assessmentSummary: questionSummaries, + sessionId: effectiveSessionId, + subsessionId: subsessionId, + sub_session_id: assessmentParams.sub_session_id, + sub_milestone_level: assessmentParams.sub_milestone_level, + apply_level: assessmentParams.apply_level, + sub_apply_level: isF3FlowActive + ? currentGameLevel + : effectiveIsShowCase + ? currentGameLevel + : undefined, + metadata: { + difficulty: "simple", + levelFailed: false, + scorePercentage: (actualCorrect / questionSummaries.length) * 100, + fuelEarned: finalFuel, + fuelRequired: requiredFuel, + missionDestination: missionDestination, + }, + }); + console.log( + "Letter Launcher assessment tracking created for level:", + currentGameLevel + ); + + // Capture familiarity_syllables from API response and store in localStorage + if ( + assessmentResponse?.data?.familiarity_syllables && + Array.isArray(assessmentResponse.data.familiarity_syllables) + ) { + setLocalData( + "confidentLetters", + JSON.stringify(assessmentResponse.data.familiarity_syllables) + ); + console.log( + "✅ Stored confidentLetters from API response (LetterLauncher - Pass):", + assessmentResponse.data.familiarity_syllables + ); + } + } catch (error) { + console.error("Error creating assessment tracking:", error); + } + } + + // Show success screen - don't advance level yet + // The SuccessScreen's Continue button will handle moving to next level or Memory Challenge + }; + + const handleLevelFail = async (questionSummariesParam, fuelParam) => { + setIsTimerRunning(false); + setIsGameComplete(true); + setLevelFailed(true); + + // CRITICAL: Always use the parameter as it contains the latest question + // The parameter is always passed from handleAnswerSelect to ensure all questions are included + const questionSummaries = questionSummariesParam || []; + // CRITICAL: Use passed fuel parameter (includes last question) instead of stale currentFuel state + const finalFuel = fuelParam !== undefined ? fuelParam : currentFuel; + + // End telemetry subsession and flush events (matches LetterGame pattern) + try { + await sessionTelemetryManager.endSubSession(); + await sessionTelemetryManager.flushAssessEventBatch(); + console.log( + "✅ Letter Launcher telemetry subsession ended and flushed (failure)" + ); + } catch (error) { + console.error( + "Error ending Letter Launcher telemetry subsession:", + error + ); + } + + if (effectiveIsShowCase && failRedirect && isF3FlowActive) { + setLocalData("letterLauncherLevelFailed", "true"); + setLocalData("letterLauncherFailedLevel", currentGameLevel.toString()); + console.log( + `Letter Launcher - Level ${currentGameLevel} failed in A${ + applyStep || 1 + }, will redirect to ${failRedirect}` + ); + } + + // Calculate total time spent + const timeSpent = levelStartTime + ? Math.round((Date.now() - levelStartTime) / 1000) + : 0; + + // Call assessment API for failed level + const currentUser = sessionManager.getCurrentUser(); + if (currentUser && questionSummaries && questionSummaries.length > 0) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + // Use sessionId prop if provided, otherwise fallback to telemetry sessionId + const effectiveSessionId = sessionId || currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + const actualCorrect = questionSummaries.filter((q) => q.isCorrect).length; + const { requiredFuel } = getFuelRequirement( + currentGameLevel, + contentCount + ); + const missionDestination = getMissionDestination(currentGameLevel); + + try { + const assessmentResponse = + await trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: `letterLauncher_${initialLanguage}`, + gameTitle: "Letter Launcher", + level: currentGameLevel, + language: initialLanguage, + totalQuestions: contentCount, + correctAnswers: actualCorrect, + totalScore: finalFuel, + timeSpent: timeSpent, + assessmentSummary: questionSummaries, + sessionId: effectiveSessionId, + subsessionId: subsessionId, + sub_session_id: assessmentParams.sub_session_id, + sub_milestone_level: assessmentParams.sub_milestone_level, + apply_level: assessmentParams.apply_level, + sub_apply_level: isF3FlowActive + ? currentGameLevel + : effectiveIsShowCase + ? currentGameLevel + : undefined, + metadata: { + difficulty: "simple", + levelFailed: true, + scorePercentage: (actualCorrect / questionSummaries.length) * 100, + fuelEarned: finalFuel, + fuelRequired: requiredFuel, + missionDestination: missionDestination, + }, + }); + console.log( + "Letter Launcher assessment tracking created for failed level:", + currentGameLevel + ); + + // Capture familiarity_syllables from API response and store in localStorage + if ( + assessmentResponse?.data?.familiarity_syllables && + Array.isArray(assessmentResponse.data.familiarity_syllables) + ) { + setLocalData( + "confidentLetters", + JSON.stringify(assessmentResponse.data.familiarity_syllables) + ); + console.log( + "✅ Stored confidentLetters from API response (LetterLauncher - Fail):", + assessmentResponse.data.familiarity_syllables + ); + } + } catch (error) { + console.error("Error creating assessment tracking:", error); + } + } + + // Show failure screen - don't call handleNext immediately + // User can choose to play again or go back + // When user clicks "Try Again", resetGame() will redirect to P1 + }; + + const handleStepComplete = async (questionSummariesParam, fuelParam) => { + // Only mark as complete and update progress when ALL questions are answered + // This function is only called when we've answered the last question + setIsGameComplete(true); + setLevelFailed(false); + + // CRITICAL: Always use the parameter as it contains the latest question + // The parameter is always passed from handleAnswerSelect to ensure all questions are included + const questionSummaries = questionSummariesParam || []; + // CRITICAL: Use passed fuel parameter (includes last question) instead of stale currentFuel state + const finalFuel = fuelParam !== undefined ? fuelParam : currentFuel; + + // Calculate total time spent + const timeSpent = levelStartTime + ? Math.round((Date.now() - levelStartTime) / 1000) + : 0; + + // End telemetry subsession and flush events (matches LetterGame pattern) + try { + await sessionTelemetryManager.endSubSession(); + await sessionTelemetryManager.flushAssessEventBatch(); + console.log( + "✅ Letter Launcher telemetry subsession ended and flushed (step complete)" + ); + } catch (error) { + console.error( + "Error ending Letter Launcher telemetry subsession:", + error + ); + } + + // Call assessment API for Practice steps + const currentUser = sessionManager.getCurrentUser(); + if (currentUser && questionSummaries && questionSummaries.length > 0) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + // Use sessionId prop if provided, otherwise fallback to telemetry sessionId + const effectiveSessionId = sessionId || currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + const actualCorrect = questionSummaries.filter((q) => q.isCorrect).length; + const { requiredFuel } = getFuelRequirement( + currentGameLevel, + contentCount + ); + const missionDestination = getMissionDestination(currentGameLevel); + + try { + const assessmentResponse = + await trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: `letterLauncher_${initialLanguage}`, + gameTitle: "Letter Launcher", + level: currentGameLevel, + language: initialLanguage, + totalQuestions: contentCount, + correctAnswers: actualCorrect, + totalScore: finalFuel, + timeSpent: timeSpent, + assessmentSummary: questionSummaries, + sessionId: effectiveSessionId, + subsessionId: subsessionId, + sub_session_id: assessmentParams.sub_session_id, + sub_milestone_level: assessmentParams.sub_milestone_level, + apply_level: assessmentParams.apply_level, + metadata: { + difficulty: "simple", + levelFailed: false, + scorePercentage: (actualCorrect / questionSummaries.length) * 100, + fuelEarned: finalFuel, + fuelRequired: requiredFuel, + missionDestination: missionDestination, + }, + }); + console.log( + "Letter Launcher assessment tracking created for Practice step" + ); + + // Capture familiarity_syllables from API response and store in localStorage + if ( + assessmentResponse?.data?.familiarity_syllables && + Array.isArray(assessmentResponse.data.familiarity_syllables) + ) { + setLocalData( + "confidentLetters", + JSON.stringify(assessmentResponse.data.familiarity_syllables) + ); + console.log( + "✅ Stored confidentLetters from API response (LetterLauncher - Practice):", + assessmentResponse.data.familiarity_syllables + ); + } + } catch (error) { + console.error("Error creating assessment tracking:", error); + } + } + + // Show completion screen - don't call handleNext immediately + // User can see results before proceeding + }; + + const resetGame = () => { + // For Apply steps with failRedirect, redirect to Practice step when level fails + // A1: Letter Launcher failure → P1 (failRedirect) + // A2: Letter Launcher failure → P1 (different from Memory Challenge which goes to P6) + // Check both state and localStorage flag to ensure we catch the failure + const levelFailedFlag = + getLocalData("letterLauncherLevelFailed") === "true"; + const shouldRedirect = + effectiveIsShowCase && + failRedirect && + isF3FlowActive && + (levelFailed || levelFailedFlag); + + if (shouldRedirect) { + const failedLevel = + getLocalData("letterLauncherFailedLevel") || currentGameLevel; + + console.log( + `Letter Launcher - Level ${failedLevel} failed in A${ + applyStep || 1 + }, redirecting to ${failRedirect}` + ); + // Clear failure flags + setLocalData("letterLauncherLevelFailed", null); + setLocalData("letterLauncherFailedLevel", null); + // Clear any sub-step state (e.g., memoryChallenge) to prevent moving to Memory Challenge + setLocalData("f3ApplySubStep", null); + // Store redirect info for Practice.jsx to handle + setLocalData("f3FlowRedirect", failRedirect); + // Reset state first + setIsGameComplete(false); + setLevelFailed(false); + // Then redirect by calling handleNext + // The handleNext in Practice.jsx will check for f3FlowRedirect first + if (handleNext) { + handleNext(); + return; + } + } + + // Reset game state for "Play Again" or "Try Again" + // IMPORTANT: This does NOT advance the flow - user retries the same step + setIsGameComplete(false); + setLevelFailed(false); + setCurrentQuestionIndex(0); + setCorrectCount(0); + setWrongCount(0); + setCurrentFuel(0); + setShowFeedback(false); + setSelectedAnswer(null); + setShowLetter(false); + setFuelEarned(null); + setQuestionStartTime(null); + setQuestionSummaries([]); + const now = Date.now(); + setLevelStartTime(now); + setIsTimerRunning(false); + setTimeRemaining(100); + // Don't reset preview states on retry - preview should only show once + const newQuestions = generateQuestions(); + setQuestions(newQuestions); + + // Start new telemetry subsession for retry (matches LetterGame pattern) + const startNewSubSession = async () => { + try { + const currentSubSession = + sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession( + gameKey, + currentGameLevel, + initialLanguage + ); + console.log( + "✅ Letter Launcher telemetry subsession started (retry):", + { + gameKey, + level: currentGameLevel, + language: initialLanguage, + } + ); + } catch (error) { + console.error( + "Error starting Letter Launcher telemetry subsession (retry):", + error + ); + } + }; + startNewSubSession(); + + if (effectiveIsShowCase) { + // Reset to the current level (don't change level - user retries same level) + setCurrentGameLevel(currentGameLevel); + // Show start screen again for Apply steps + effectiveSetStartShowCase(false); + } + }; + + if (!sessionInitialized || questions.length === 0 || isLoadingLevel) { + return ( + +
+

Loading game...

+
+
+ ); + } + + // Show story preview after countdown (check this first) + // Only show for P1 step + const isP1StepForPreview = + isF3FlowActive && + f3FlowStep?.step?.type === "P" && + f3FlowStep?.step?.step === 1; + if (showStoryPreview && initialLanguage && isP1StepForPreview) { + console.log( + "[LetterLauncherMechanics] Rendering story preview, level:", + currentGameLevel + ); + return ( + + + +
+ +
+ { + setShowStoryPreview(false); + setShowPreview(false); + handleGameBack(); + }} + level={currentGameLevel} + hideHeader={true} + /> +
+
+
+
+
+ ); + } + + // Show countdown when first opening game (before game starts) + // Only show for P1 step + const shouldShowCountdown = + showPreview && + initialLanguage && + !showStoryPreview && + isP1StepForPreview && + !level19HasProgress; + + if (shouldShowCountdown) { + return ( + + + +
+
+
+ +
+
+ +
+
+
+
+
+
+ ); + } + + // Use locked question if audio is playing, otherwise use questions array + // This prevents questions array changes from affecting the question that audio is playing for + const usingLockedQuestion = + isPlayingAudioRef.current && + lockedQuestionForAudioRef.current && + lockedQuestionForAudioRef.current.questionIndex === currentQuestionIndex; + const currentQuestion = usingLockedQuestion + ? lockedQuestionForAudioRef.current + : questions[currentQuestionIndex]; + + // Get fuel requirements and mission destination + const { requiredFuel, maxFuel } = getFuelRequirement( + currentGameLevel, + contentCount + ); + const missionDestination = getMissionDestination(currentGameLevel); + + // Show completion screen when game is complete + if (isGameComplete) { + const finalFuel = currentFuel; + const totalQuestions = questions.length; + const totalCorrect = correctCount; + + // Check if level passed (for Apply steps, check fuel; for Practice, check if all questions answered) + // For Apply steps: must have enough fuel to pass + // For Practice steps: must have at least one correct answer + const hasPassed = effectiveIsShowCase + ? finalFuel >= requiredFuel + : totalCorrect > 0; // Practice steps pass if at least one correct + + // Debug logging + console.log("Letter Launcher - Completion Screen Check:", { + isGameComplete, + levelFailed, + finalFuel, + requiredFuel, + hasPassed, + effectiveIsShowCase, + totalCorrect, + totalQuestions, + }); + + // IMPORTANT: Check actual fuel value first - if user has enough fuel, they should pass + // Override levelFailed state if fuel is actually sufficient + const actuallyHasEnoughFuel = finalFuel >= requiredFuel; + const shouldShowFailure = levelFailed && !actuallyHasEnoughFuel; + + // If user has enough fuel but levelFailed is true, correct the state + if (actuallyHasEnoughFuel && levelFailed) { + console.warn( + `Letter Launcher - State mismatch: levelFailed is true but fuel ${finalFuel} >= required ${requiredFuel}. Correcting to pass.` + ); + setLevelFailed(false); + } + + // Show failure screen only if user actually failed (not enough fuel) + if (shouldShowFailure) { + // User actually failed - show failure screen + // Show failure screen with fuel display + return ( + +
+ +
+
+ ); + } + + // Show success screen + const starsEarned = effectiveIsShowCase + ? finalFuel < requiredFuel + ? 1 + : finalFuel - requiredFuel > (maxFuel - requiredFuel) / 2 + ? 3 + : 2 + : 3; // Practice steps always get 3 stars + + return ( + + +
+ { + // Continue to next step/level - clear completion state + setIsGameComplete(false); + setLevelFailed(false); + + // For Apply steps: check if there are more levels or if all levels are complete + if (effectiveIsShowCase && endLevel) { + if (currentGameLevel >= endLevel) { + // All levels passed - move to Memory Challenge + // The handleNext callback from Practice.jsx will handle moving to Memory Challenge + // and then redirecting to passRedirect after Memory Challenge completes + console.log( + `Letter Launcher - All ${endLevel} levels passed (Level ${currentGameLevel}), moving to Memory Challenge` + ); + // Clear any redirect flags + setLocalData("f3FlowRedirect", null); + // Just call handleNext - it will handle moving to Memory Challenge + // Memory Challenge will then handle redirecting to passRedirect (e.g., P6) + if (handleNext) { + handleNext(); + } + return; + } else { + // More levels to complete - advance to next level + const nextLevel = currentGameLevel + 1; + console.log( + `Letter Launcher - Level ${currentGameLevel} passed, moving to Level ${nextLevel}` + ); + // Reset game state for next level + setCurrentGameLevel(nextLevel); + setCurrentQuestionIndex(0); + setCorrectCount(0); + setWrongCount(0); + setShowFeedback(false); + setSelectedAnswer(null); + setShowLetter(false); + setFuelEarned(null); + setCurrentFuel(0); + setQuestionSummaries([]); + const now = Date.now(); + setLevelStartTime(now); + setIsTimerRunning(false); + const newQuestions = generateQuestions(); + setQuestions(newQuestions); + + // Start new telemetry subsession for next level (matches LetterGame pattern) + const startNextLevelSubSession = async () => { + try { + const currentSubSession = + sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession( + gameKey, + nextLevel, + initialLanguage + ); + console.log( + "✅ Letter Launcher telemetry subsession started (next level):", + { + gameKey, + level: nextLevel, + language: initialLanguage, + } + ); + } catch (error) { + console.error( + "Error starting Letter Launcher telemetry subsession (next level):", + error + ); + } + }; + startNextLevelSubSession(); + + // Reset start screen for next level + if (effectiveIsShowCase) { + effectiveSetStartShowCase(false); + } + return; + } + } + + // For Practice steps: advance F3 flow and save progress for the next step + if (isF3FlowActive && f3FlowStep?.step) { + // IMPORTANT: Set flag FIRST to prevent duplicate addLesson calls + // This must be set before calling handleNext + setLocalData("f3FlowAdvancedByLetterLauncher", "true"); + + // Get current F3 flow step (the step that was just completed) + const currentF3FlowStep = getF3FlowStep(); + + // Advance F3 flow to the next step + const nextStep = advanceF3Flow(); + + // Get updated F3 flow step after advancement (this is the NEXT step where user will resume) + const updatedF3FlowStep = getF3FlowStep(); + + if (updatedF3FlowStep.step) { + // Save progress to backend for the NEXT step (where user will resume) + // This matches the pattern used in F1 and F2 flows + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + const totalF3Steps = F3_FLOW.length; + + // Calculate progress for the NEXT step (where user will resume) + // Progress = (nextStepIndex + 1) / totalSteps * 100 + const nextStepProgress = Math.round( + ((updatedF3FlowStep.index + 1) / totalF3Steps) * 100 + ); + + try { + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: (updatedF3FlowStep.index + 1).toString(), // Convert to 1-indexed for backend (matches F1/F2 pattern) + progress: nextStepProgress, // Progress for the next step + language: lang, + milestoneLevel: "B", + subMilestoneLevel: "F3", + duration: calculateF3Duration(), + applyLevel: getF3StepTitle(updatedF3FlowStep.index), + }); + console.log( + "F3 flow progress saved by LetterLauncherMechanics (after step completion):", + { + completedStepIndex: currentF3FlowStep.index, + nextStepIndex: updatedF3FlowStep.index, + lessonSaved: (updatedF3FlowStep.index + 1).toString(), // 1-indexed + progress: nextStepProgress, + } + ); + } catch (e) { + console.error( + "Error storing F3 flow progress in LetterLauncherMechanics:", + e + ); + } + + // Update points for F3 flow based on contentCount + if (!localStorage.getItem("contentSessionId")) { + try { + const f3Config = levelGetContent[lang]?.["F3"]; + const completedStepContent = + f3Config?.[currentF3FlowStep.index]; + const isApplyStep = + completedStepContent?.title?.startsWith("A"); + + let pointsToAdd; + + if (isApplyStep) { + // For Apply steps: F3 A1 = 20 × 3 levels = 60 points, F3 A2 = contentCount (45) + if ( + completedStepContent?.letterLauncherContentCount + ) { + const pointsPerLevel = + completedStepContent.letterLauncherContentCount; + const numLevels = + completedStepContent?.letterLauncherEndLevel - + completedStepContent?.letterLauncherLevel + + 1; + pointsToAdd = pointsPerLevel * numLevels; + } else { + pointsToAdd = + completedStepContent?.contentCount || 1; + } + } else { + // For non-Apply steps, use contentCount + pointsToAdd = + completedStepContent?.letterLauncherContentCount || + completedStepContent?.memoryChallengeContentCount || + completedStepContent?.readAloudContentCount || + completedStepContent?.contentCount || + 1; + } + + await addPointer(pointsToAdd, "B"); + } catch (error) { + console.error("Error updating F3 flow points:", error); + } + } + + // Update local storage with NEXT step progress + const updatedPracticeProgress = { + currentQuestion: 0, + currentPracticeProgress: nextStepProgress, + currentPracticeStep: updatedF3FlowStep.index, + }; + setLocalData( + "practiceProgress", + JSON.stringify(updatedPracticeProgress) + ); + + // Update parent state if setProgressData is provided + if ( + setProgressData && + typeof setProgressData === "function" + ) { + setProgressData(updatedPracticeProgress); + } + + // Reset currentQuestion state to 0 so handleNext doesn't increment + if ( + setCurrentQuestion && + typeof setCurrentQuestion === "function" + ) { + setCurrentQuestion(0); + } + } + } + + // Call handleNext to move to next step (it will skip addLesson if flag is set) + if (handleNext) { + handleNext(); + } + }} + continueButtonText="Continue" + /> +
+
+
+ ); + } + + return ( + + +
+ {/* Header */} +
+ + +
+

+ Letter Launcher +

+
+ + {/* Spacer to balance layout */} +
+
+ + {/* Main Content Card */} +
+ {/* Fuel Progress - Only show if showProgress is true */} + {showProgress && ( +
+ +
+ )} + + {/* Game Area */} +
+
+ + + {currentQuestion && + (() => { + const questionToPass = { + ...currentQuestion, + displayedLetter: showLetter + ? currentQuestion.displayedLetter + : "", + }; + + // CRITICAL: Store the exact question being rendered in the ref + // This ensures validation uses the same question that's displayed on screen + // We do this in render (not in async audio function) to match what's actually shown + if ( + showLetter && + displayedQuestionRef.current?.questionIndex !== + currentQuestionIndex + ) { + displayedQuestionRef.current = { + ...currentQuestion, + questionIndex: currentQuestionIndex, + }; + } + + // Only log when question actually changes (not on every re-render) + const questionKey = `${currentQuestionIndex}-${currentQuestion.audioLetter}-${currentQuestion.displayedLetter}-${showLetter}`; + if (lastLoggedQuestionRef.current !== questionKey) { + lastLoggedQuestionRef.current = questionKey; + } + return ( + + ); + })()} + + +
+
+
+
+
+
+ ); +}; + +const LetterLauncherMechanics = (props) => { + return ; +}; + +export default LetterLauncherMechanics; diff --git a/src/components/Practice/McqFlow.jsx b/src/components/Practice/McqFlow.jsx index af8cbdde..acdea782 100644 --- a/src/components/Practice/McqFlow.jsx +++ b/src/components/Practice/McqFlow.jsx @@ -40,8 +40,7 @@ import VoiceAnalyser from "../../utils/VoiceAnalyser"; import correctSound from "../../assets/correct.wav"; import wrongSound from "../../assets/audio/wrong.wav"; import { Modal } from "@mui/material"; -import ZoomInIcon from "@mui/icons-material/ZoomIn"; -import CloseIcon from "@mui/icons-material/Close"; +import ZoomableImage from "./ZoomableImage"; import { filterBadWords } from "@tekdi/multilingual-profanity-filter"; import { @@ -107,7 +106,6 @@ const McqFlow = ({ const [recAudio, setRecAudio] = useState(""); const [completeAudio, setCompleteAudio] = useState(null); const [imageData, setImageData] = useState({}); - const [zoomOpen, setZoomOpen] = useState(false); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); const [abusiveFound, setAbusiveFound] = useState(false); @@ -340,135 +338,20 @@ const McqFlow = ({ // alt="Children's Day" // style={{ width: "250px", height: "250px", borderRadius: "15px" }} // /> - - contentImage setZoomOpen(true)} // Open modal on click - /> - - {/* Subtle gradient overlay across the top */} - - {/* Zoom icon positioned in the top-left corner */} - {/* setZoomOpen(true)} - sx={{ color: "white", fontSize: "22px", cursor: "pointer" }} - /> */} - - setZoomOpen(true)} - height={"65px"} - width={"65px"} - /> - - - - )} - - setZoomOpen(false)} - sx={{ - display: "flex", - alignItems: "center", - justifyContent: "center", - zIndex: "99999", - }} - > - - {/* Subtle gradient overlay at the top of the zoomed image */} - - {/* Close icon positioned within the gradient overlay */} - {/* setZoomOpen(false)} - sx={{ - color: "white", - fontSize: "24px", - cursor: "pointer", - backgroundColor: "rgba(0, 0, 0, 0.5)", - borderRadius: "50%", - padding: "4px", - }} - /> */} - setZoomOpen(false)} - style={{ marginTop: "20px", cursor: "pointer" }} - /> - - - Zoomed content - - + iconPosition="bottom-right" + useCustomIcon={true} + /> + )} {!conversation?.instruction?.content[0]?.text && ( { const [words, setWords] = useState([]); const [sentences, setSentences] = useState([]); - const [zoomOpen, setZoomOpen] = useState(false); const [selectedWord, setSelectedWord] = useState(""); // const [loading, setLoading] = useState(false); const [shake, setShake] = useState(false); @@ -299,110 +296,14 @@ const Mechanics2 = ({ }} > {image?.split("/")?.[4] && ( - - setZoomOpen(true)} - src={image} - style={{ - borderRadius: "20px", - maxWidth: "100%", - height: "clamp(150px, 20vw, 220px)", - }} - alt="" - /> - - {/* Subtle gradient overlay across the top */} - - {/* Zoom icon positioned in the top-left corner */} - setZoomOpen(true)} - sx={{ - color: "white", - fontSize: "22px", - cursor: "pointer", - }} - /> - - - )} - setZoomOpen(false)} - sx={{ - display: "flex", - alignItems: "center", - justifyContent: "center", - marginTop: isMobile ? "0px" : isTablet ? "45px" : "0px", - }} - > - - {/* Subtle gradient overlay at the top of the zoomed image */} - - {/* Close icon positioned within the gradient overlay */} - setZoomOpen(false)} - sx={{ - color: "white", - fontSize: isMobile ? "20px" : "24px", - cursor: "pointer", - backgroundColor: "rgba(0, 0, 0, 0.5)", - borderRadius: "50%", - padding: "4px", - }} - /> - - - Zoomed content - - + /> + )} React.createRef()) ); - const [zoomOpen, setZoomOpen] = useState(false); const questionAudioRef = useRef(); const [playingIndex, setPlayingIndex] = useState(null); const [selectedOption, setSelectedOption] = useState(null); // Add state to track selected radio button @@ -205,111 +202,8 @@ const Mechanics5 = ({ {/* Image with full-width gradient overlay on top */} {image?.split("/")?.[4] && ( - - contentImage setZoomOpen(true)} // Open modal on click - /> - - {/* Subtle gradient overlay across the top */} - - {/* Zoom icon positioned in the top-left corner */} - setZoomOpen(true)} - sx={{ color: "white", fontSize: "22px", cursor: "pointer" }} - /> - - + )} - - {/* Modal for zoomed image with gradient and close icon */} - setZoomOpen(false)} - sx={{ - display: "flex", - alignItems: "center", - justifyContent: "center", - }} - > - - {/* Subtle gradient overlay at the top of the zoomed image */} - - {/* Close icon positioned within the gradient overlay */} - setZoomOpen(false)} - sx={{ - color: "white", - fontSize: "24px", - cursor: "pointer", - backgroundColor: "rgba(0, 0, 0, 0.5)", - borderRadius: "50%", - padding: "4px", - }} - /> - - - Zoomed content - - diff --git a/src/components/Practice/Mechanics7.jsx b/src/components/Practice/Mechanics7.jsx index 293ae336..8ae0d654 100644 --- a/src/components/Practice/Mechanics7.jsx +++ b/src/components/Practice/Mechanics7.jsx @@ -45,7 +45,7 @@ import { callTelemetryApi, } from "../../utils/apiUtil"; import AudioTooltipModal from "./AudioTooltipModal"; -import { loadTranscriber } from "../../utils/transcriber"; +import ZoomableImage from "./ZoomableImage"; import { doubleMetaphone } from "double-metaphone"; import loadingJson from "../../assets/loadingJson.json"; import Lottie from "lottie-react"; @@ -97,6 +97,7 @@ const Mechanics7 = ({ vocabCount, wordCount, multilingual, + enableMultilingual = true, }) => { const [words, setWords] = useState( type === "word" ? [] : ["Friend", "She is", "My"] @@ -134,6 +135,23 @@ const Mechanics7 = ({ } = useSpeechRecognition(); const transcriptRef = useRef(""); + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + let progressDatas = getLocalData("practiceProgress"); //const virtualId = String(getLocalData("virtualId")); @@ -263,7 +281,7 @@ const Mechanics7 = ({ // ? currentImg?.completeWord // : currentImg?.syllablesAudio?.[stepIndex]?.name || ""; - const currentAudio = parentWords?.syllable?.[stepIndex]?.audio_url || null; + const currentAudio = currentImg?.audioUrl || null; const [stepsIndex, setStepsIndex] = useState(0); //console.log("wordSyl", currentText); @@ -286,9 +304,7 @@ const Mechanics7 = ({ }; mediaRecorder.onstop = async () => { - console.log("⛔ Recording stopped."); if (chunksRef.current.length === 0) { - console.warn("❗ No data to create blob."); return; } @@ -302,17 +318,9 @@ const Mechanics7 = ({ if (isLastSyllable) { try { setIsLoading(true); - const transcriber = await loadTranscriber(); - //console.log("Transcriber is:", transcriber); - const audioUrl = URL.createObjectURL(audioBlob); - const output = await transcriber(audioUrl, { - chunk_length_s: 20, - stride_length_s: 5, - task: "transcribe", - language: "en", - }); - - const transcripts = sanitize(output.text); + + // Use browser speech recognition transcript (already captured during recording) + const transcripts = sanitize(transcriptRef.current || ""); const target = sanitize(currentText); const isCorrect = transcripts.includes(target) || @@ -349,8 +357,6 @@ const Mechanics7 = ({ ) { //console.log("🛑 Stopping recording..."); mediaRecorderRef.current.stop(); - } else { - console.warn("❗ Recorder already inactive or null."); } }; @@ -464,7 +470,7 @@ const Mechanics7 = ({ setIsMicOn(false); console.error("Speech recognition error:", event.error); if (event.error === "no-speech") { - console.log("No Speech!"); + // No speech detected } else if (event.error === "aborted") { recognitionInstance.start(); } @@ -582,8 +588,6 @@ const Mechanics7 = ({ if (matchedSyllable) { playAudio(matchedSyllable.audio); - } else { - console.warn(`No audio found for the syllable: ${elem}`); } }; @@ -942,7 +946,6 @@ const Mechanics7 = ({ }, }} callback={(data) => { - console.log('Joyride callback:', data); if (data.status === 'finished' || data.status === 'skipped') { setRun(false); } @@ -974,12 +977,12 @@ const Mechanics7 = ({ {match && {match}} {after && {after}} - pencil @@ -1086,9 +1089,9 @@ const Mechanics7 = ({ {currentText} )} - {showMultiLingual && ( + {showMultiLingual && enableMultilingual && ( )} - {isRecorded && !showMultiLingual && ( + {isRecorded && (!showMultiLingual || !enableMultilingual) && ( graph - {showMultiLingual && ( + {showMultiLingual && enableMultilingual && ( graph )} - {showMultiLingual && ( + {showMultiLingual && enableMultilingual && ( )} - {!isRecording && !isRecorded && !showMultiLingual && ( - - {isPlaying ? ( -
- - - -
- ) : ( -
- { - playWordAudio( - `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${currentAudio}` - ); - }} - > - - -
- )} + {!isRecording && + !isRecorded && + (!showMultiLingual || !enableMultilingual) && ( { - setIsRecording(true); - startRecording(currentText); - //startAudioRecording(); + maskBorderWidth: 6, + gap: 5, + height: "250px", }} > + {isPlaying ? ( +
+ + + +
+ ) : ( +
+ { + playWordAudio( + `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${currentAudio}` + ); + }} + > + + +
+ )} - { + setIsRecording(true); + startRecording(currentText); + //startAudioRecording(); }} > - + + + + - - )} + )} - {isRecording && !showMultiLingual && ( + {isRecording && (!showMultiLingual || !enableMultilingual) && ( { - if (isLastSyllable) { + if (isLastSyllable && enableMultilingual) { setShowMultiLingual(true); + } else if (isLastSyllable && !enableMultilingual) { + // If multilingual is disabled, proceed to next content + const newWordData = { + original_text: currentText, + content_id: contentId, + milestone_level: "m1", + practice_level: currentLevel, + session_id: sessionId, + practiced: true, + learned: isWordCorrect ? true : false, + subsession_id: "session_123", + }; + + callTelemetry(); + setLocalData("correctPracticeWords", [ + ...(correctPracticeWords || []), + newWordData, + ]); + handleNext(); + setStepIndex(0); + setIsRecorded(false); } else { setStepIndex((prev) => prev + 1); setIsRecorded(false); diff --git a/src/components/Practice/MemoryChallenge.jsx b/src/components/Practice/MemoryChallenge.jsx new file mode 100644 index 00000000..2468c9b2 --- /dev/null +++ b/src/components/Practice/MemoryChallenge.jsx @@ -0,0 +1,473 @@ +import React, { useState, useEffect, useRef } from "react"; +import MainLayout from "../Layouts.jsx/MainLayout"; +import { Box, Typography, Button, CircularProgress, Grid } from "@mui/material"; +import { getLocalData, setLocalData } from "../../utils/constants"; +import { useNavigate } from "react-router-dom"; +import { ArrowRight, RotateCcw } from "lucide-react"; +import correctSound from "../../assets/correct.wav"; +import wrongSound from "../../assets/audio/wrong.wav"; + +/** + * Memory Challenge Component + * Sequence recall game for F3 flow Apply steps + * + * Props: + * - level: Starting level (1, 2, 3) + * - endLevel: End level (usually 3) + * - contentCount: Number of sequences per level (usually 5) + * - handleNext: Callback when step completes + * - handleBack: Callback for back navigation + * - applyStep: Apply step number (1 or 2) + * - failRedirect: Redirect target on failure + * - passRedirect: Redirect target on pass + * - isF3FlowActive: Whether F3 flow is active + * - f3FlowStep: Current F3 flow step info + */ +const MemoryChallenge = ({ + level = 1, + endLevel = 3, + contentCount = 5, // 5 sequences per level + handleNext, + handleBack, + applyStep, + failRedirect, + passRedirect, + isF3FlowActive, + f3FlowStep, + header = "Memory Challenge", + points = 0, + steps = 5, + currentStep = 1, + progressData, + showProgress = true, + background = "#FFB31F", + enableNext, + setEnableNext, + loading, + setOpenMessageDialog, + vocabCount, + wordCount, + showTimer = false, + milestoneLevel, + setProgressData, + setCurrentQuestion, +}) => { + const navigate = useNavigate(); + const lang = getLocalData("lang") || "en"; + const [currentLevel, setCurrentLevel] = useState(level || 1); + const [currentSequenceIndex, setCurrentSequenceIndex] = useState(0); + const [sequences, setSequences] = useState([]); + const [currentSequence, setCurrentSequence] = useState(null); + const [isShowingSequence, setIsShowingSequence] = useState(true); + const [userInput, setUserInput] = useState([]); + const [correctCount, setCorrectCount] = useState(0); + const [wrongCount, setWrongCount] = useState(0); + const [isComplete, setIsComplete] = useState(false); + const audioRef = useRef(null); + const sequenceTimerRef = useRef(null); + + // Generate sequences: 2 meaningful words + 3 non-word sequences per level + useEffect(() => { + const generateSequences = () => { + const generatedSequences = []; + + // Meaningful words + const meaningfulWords = [ + ["cat", "dog"], + ["bag", "tap"], + ["sun", "moon"], + ["hat", "cap"], + ["run", "jump"], + ]; + + // Non-word sequences + const nonWords = [ + ["b", "a", "t"], + ["p", "i", "g"], + ["c", "u", "p"], + ["d", "o", "g"], + ["f", "a", "n"], + ]; + + for (let i = 0; i < contentCount; i++) { + if (i < 2) { + // First 2 are meaningful words + const wordPair = + meaningfulWords[Math.floor(Math.random() * meaningfulWords.length)]; + generatedSequences.push({ + id: i, + sequence: wordPair, + type: "meaningful", + correctAnswer: wordPair, + }); + } else { + // Last 3 are non-word sequences + const nonWord = nonWords[Math.floor(Math.random() * nonWords.length)]; + generatedSequences.push({ + id: i, + sequence: nonWord, + type: "nonword", + correctAnswer: nonWord, + }); + } + } + + setSequences(generatedSequences); + if (generatedSequences.length > 0) { + setCurrentSequence(generatedSequences[0]); + setIsShowingSequence(true); + // Show sequence for 3 seconds + sequenceTimerRef.current = setTimeout(() => { + setIsShowingSequence(false); + setUserInput([]); + }, 3000); + } + }; + + generateSequences(); + }, [contentCount, currentLevel]); + + const handleSequenceClick = (item) => { + if (isShowingSequence) return; // Don't allow input while showing sequence + + setUserInput((prev) => [...prev, item]); + }; + + const handleSubmit = () => { + if (!currentSequence) return; + + const isCorrect = + JSON.stringify(userInput) === + JSON.stringify(currentSequence.correctAnswer); + + if (isCorrect) { + playSound(correctSound); + setCorrectCount((prev) => prev + 1); + } else { + playSound(wrongSound); + setWrongCount((prev) => prev + 1); + } + + // Move to next sequence + if (currentSequenceIndex < sequences.length - 1) { + setCurrentSequenceIndex((prev) => { + const nextIndex = prev + 1; + setCurrentSequence(sequences[nextIndex]); + setIsShowingSequence(true); + setUserInput([]); + // Show next sequence for 3 seconds + if (sequenceTimerRef.current) { + clearTimeout(sequenceTimerRef.current); + } + sequenceTimerRef.current = setTimeout(() => { + setIsShowingSequence(false); + }, 3000); + return nextIndex; + }); + } else { + // Completed all sequences in this level + handleLevelComplete(); + } + }; + + const handleLevelComplete = () => { + // Check pass criteria: > 80% accuracy + const accuracy = (correctCount / sequences.length) * 100; + + if (accuracy > 80) { + // Level passed + if (currentLevel < endLevel) { + // Move to next level + setCurrentLevel((prev) => prev + 1); + setCurrentSequenceIndex(0); + setCorrectCount(0); + setWrongCount(0); + setUserInput([]); + setIsComplete(false); + // Sequences will be regenerated by useEffect + } else { + // All levels passed + setIsComplete(true); + setTimeout(() => { + if (handleNext) { + handleNext(); + } + }, 2000); + } + } else { + // Level failed - redirect to failRedirect + if (failRedirect && handleNext) { + setTimeout(() => { + handleNext(); + }, 2000); + } + } + }; + + const playSound = (soundFile) => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + audioRef.current = new Audio(soundFile); + audioRef.current.play().catch((err) => { + console.log("Audio play error:", err); + }); + }; + + const handleRetry = () => { + setCurrentSequenceIndex(0); + setCorrectCount(0); + setWrongCount(0); + setUserInput([]); + setIsComplete(false); + // Sequences will be regenerated by useEffect + }; + + useEffect(() => { + return () => { + if (sequenceTimerRef.current) { + clearTimeout(sequenceTimerRef.current); + } + }; + }, []); + + if (!currentSequence && sequences.length === 0) { + return ( + {}} + level={milestoneLevel || "B"} + flowNames={[]} + activeFlow={isF3FlowActive ? `A${f3FlowStep?.step?.step || 1}` : ""} + progressData={progressData} + showProgress={showProgress} + points={points} + vocabCount={vocabCount} + wordCount={wordCount} + > + + + + + ); + } + + const accuracy = + sequences.length > 0 ? (correctCount / sequences.length) * 100 : 0; + + return ( + {}} + level={milestoneLevel || "B"} + flowNames={[]} + activeFlow={isF3FlowActive ? `A${f3FlowStep?.step?.step || 1}` : ""} + progressData={progressData} + showProgress={showProgress} + points={points} + vocabCount={vocabCount} + wordCount={wordCount} + > + + + + Level {currentLevel} / {endLevel} + + + Sequence {currentSequenceIndex + 1} / {sequences.length} + + + Correct: {correctCount} | Wrong: {wrongCount} + + + Accuracy: {accuracy.toFixed(0)}% + + + + {isShowingSequence && currentSequence && ( + + + Watch and Remember + + + {currentSequence.sequence.map((item, idx) => ( + + {item} + + ))} + + + Sequence will disappear in a moment... + + + )} + + {!isShowingSequence && currentSequence && ( + + + Recreate the Sequence + + + + {userInput.map((item, idx) => ( + + {item} + + ))} + + + + {currentSequence.sequence.map((item, idx) => ( + + + + ))} + + + + + )} + + {isComplete && ( + + + All Levels Passed! + + + Final Accuracy: {accuracy.toFixed(0)}% + + + + )} + + + ); +}; + +export default MemoryChallenge; diff --git a/src/components/Practice/MemoryChallengeMechanics.jsx b/src/components/Practice/MemoryChallengeMechanics.jsx new file mode 100644 index 00000000..54fde1c4 --- /dev/null +++ b/src/components/Practice/MemoryChallengeMechanics.jsx @@ -0,0 +1,1095 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import MainLayout from "../Layouts.jsx/MainLayout"; +import { + getLocalData, + setLocalData, + practiceSteps, + levelGetContent, +} from "../../utils/constants"; +import { addLesson } from "../../services/orchestration/orchestrationService"; +import { getF3FlowStep, advanceF3Flow, F3_FLOW } from "../../RFlow/F3"; + +// Import from library +import { + MemoryGameCore, + LanguageProvider, + AudioLanguageProvider, + sessionManager, + sessionTelemetryManager, + memoryGameDataLoader, + SuccessScreen, + TryAgain, +} from "../../lib/axl-explorations/src/lib/index"; +import { ClockwiseTimer } from "../../lib/axl-explorations/src/components/ClockwiseTimer"; +import { trackingAssessmentService } from "../../lib/axl-explorations/src/utils/trackingAssessmentService"; + +/** + * Wrapper component that integrates axl-explorations MemoryGameCore + * into the Practice.jsx mechanics system for F3 flow Memory Challenge + */ +const MemoryChallengeMechanicsContent = ({ + page, + setPage, + level, // Starting level (1, 2, 3) + header, + points, + steps, + currentStep, + progressData, + showProgress, + background, + handleNext, + handleBack, + enableNext, + setEnableNext, + isShowCase, + loading, + setOpenMessageDialog, + vocabCount, + wordCount, + showTimer, + milestoneLevel, + endLevel = 3, // End level (usually 3) + startShowCase, + setStartShowCase, + setProgressData, + setCurrentQuestion, + applyStep, + failRedirect, + passRedirect, + isF3FlowActive, + f3FlowStep, + contentCount = 5, // Number of sequences per level + sessionId, // Optional: Session ID from parent +}) => { + const [currentGameLevel, setCurrentGameLevel] = useState(level || 1); + const [isGameComplete, setIsGameComplete] = useState(false); + const [levelPassed, setLevelPassed] = useState(false); + const [levelFailed, setLevelFailed] = useState(false); + const [sessionInitialized, setSessionInitialized] = useState(false); + const navigate = useNavigate(); + const lang = getLocalData("lang") || "en"; + + // Map language to library Language type + const initialLanguage = + lang === "en" + ? "en" + : lang === "te" + ? "te" + : lang === "kn" + ? "kn" + : lang === "mr" + ? "mr" + : lang === "hi" + ? "hi" + : "en"; + const initialAudioLanguage = initialLanguage; + + useEffect(() => { + localStorage.setItem("selectedLanguage", initialLanguage); + localStorage.setItem("selectedAudioLanguage", initialAudioLanguage); + }, [initialLanguage, initialAudioLanguage]); // Initialize telemetry session before game starts + useEffect(() => { + const initializeSession = async () => { + try { + const currentUser = sessionManager.getCurrentUser(); + let userId = "anonymous"; + + if (currentUser && currentUser.username) { + userId = currentUser.username; + } else { + const storedUser = + localStorage.getItem("user") || localStorage.getItem("username"); + if (storedUser) { + userId = storedUser; + } + } + + const currentSession = sessionTelemetryManager.getCurrentSession(); + if (!currentSession || !currentSession.isActive) { + await sessionTelemetryManager.startUserSession(userId); + console.log( + "✅ Telemetry session initialized for Memory Challenge game" + ); + } + + setSessionInitialized(true); + } catch (error) { + console.warn("Failed to initialize telemetry session:", error); + setSessionInitialized(true); + } + }; + + const timeoutId = setTimeout(() => { + console.warn("Session initialization timeout - proceeding anyway"); + setSessionInitialized(true); + }, 3000); + + document.body.classList.add("memory-challenge-active"); + + initializeSession(); + + return () => { + document.body.classList.remove("memory-challenge-active"); + clearTimeout(timeoutId); + }; + }, []); + + const handleGameBack = () => { + if (handleBack) { + handleBack(); + } + }; + + // Generate sequences: 2 meaningful words + 3 non-word sequences per level + const generateSequences = () => { + const supportedLanguage = + initialLanguage === "en" || + initialLanguage === "te" || + initialLanguage === "kn" || + initialLanguage === "mr" || + initialLanguage === "hi" + ? initialLanguage + : "en"; + + // Use memoryGameDataLoader to generate sequences + const sequences = memoryGameDataLoader.generateMemoryQuestions( + supportedLanguage, + currentGameLevel, + "simple", + contentCount + ); + + return sequences.map((seq, idx) => ({ + sequence: seq.sequence, + display: seq.display || seq.sequence.join(""), + complexity: seq.complexity || "simple", + language: seq.language || initialLanguage, + })); + }; + + const [sequences, setSequences] = useState([]); + const [currentSequenceIndex, setCurrentSequenceIndex] = useState(0); + const [showSequence, setShowSequence] = useState(true); + const [userInput, setUserInput] = useState([]); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [correctCount, setCorrectCount] = useState(0); + const [wrongCount, setWrongCount] = useState(0); + const [completedSequences, setCompletedSequences] = useState(0); // Track completed sequences for progress + const [timeRemaining, setTimeRemaining] = useState(0); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [showTimeoutMessage, setShowTimeoutMessage] = useState(false); + const [currentLetterOptions, setCurrentLetterOptions] = useState([]); + const [questionSummaries, setQuestionSummaries] = useState([]); // Track question summaries for assessment + const [levelStartTime, setLevelStartTime] = useState(null); // Track level start time + const [totalTimeSpent, setTotalTimeSpent] = useState(0); // Track total time spent + + // Get F3 flow assessment parameters from config + const getF3AssessmentParams = () => { + if (!isF3FlowActive || !f3FlowStep?.step) { + return { + sub_session_id: undefined, + sub_milestone_level: undefined, + apply_level: undefined, + sub_apply_level: undefined, + }; + } + + // Get sub_session_id from telemetry + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sub_session_id = currentSubSession?.subSessionId; + + // For F3 flow, sub_milestone_level is always "F3" + const sub_milestone_level = "F3"; + + // Get step title from F3 config + const f3Config = levelGetContent[lang]?.["F3"]; + const f3StepConfig = + f3Config && Array.isArray(f3Config) && f3Config[f3FlowStep.index] + ? f3Config[f3FlowStep.index] + : null; + const stepTitle = + f3StepConfig?.title || + (f3FlowStep.step?.type === "A" + ? `A${f3FlowStep.step?.step}` + : f3FlowStep.step?.type === "P" + ? `P${f3FlowStep.step?.step}` + : null); + + // Determine apply_level - use step title for all F3 flow steps + const apply_level = stepTitle || undefined; + + return { + sub_session_id, + sub_milestone_level, + apply_level, + // sub_apply_level will be set dynamically based on currentGameLevel + }; + }; + + const assessmentParams = getF3AssessmentParams(); + + useEffect(() => { + if (sessionInitialized) { + const generatedSequences = generateSequences(); + setSequences(generatedSequences); + + // Get letter options from the first sequence + if (generatedSequences.length > 0) { + const allLetters = new Set(); + generatedSequences.forEach((seq) => { + seq.sequence.forEach((letter) => allLetters.add(letter)); + }); + setCurrentLetterOptions(Array.from(allLetters)); + } + + // Initialize level start time and reset question summaries + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + setTotalTimeSpent(0); + } + }, [sessionInitialized, currentGameLevel, contentCount]); + + // Get time limit for memory sequences based on complexity and level + const getTimeLimit = (complexity = "simple") => { + // Base time limits by complexity for memory sequences + const baseTime = { + basic: 8, + intermediate: 6, + advanced: 5, + expert: 4, + master: 3, + simple: 6, // Default for our use case + }; + + // Additional level-based time reduction for higher levels + const levelBonus = Math.max(0, Math.floor((currentGameLevel - 1) * 0.2)); + + return Math.max(3, (baseTime[complexity] || 6) - levelBonus); + }; + + // Timer effect for memory sequence display phase + useEffect(() => { + const currentSeq = sequences[currentSequenceIndex]; + if (currentSeq && showSequence && !showFeedback && sequences.length > 0) { + // Start timer when showing sequence + const timeLimit = getTimeLimit("simple"); + setTimeRemaining(timeLimit); + setIsTimerRunning(true); + setShowTimeoutMessage(false); // Reset timeout message for new sequence + + const timer = setInterval(() => { + setTimeRemaining((prev) => { + if (prev <= 1) { + // Time's up - automatically move to input phase + setIsTimerRunning(false); + setShowSequence(false); + setShowTimeoutMessage(true); // Show timeout message + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } else if (!showSequence) { + // Reset timer when sequence is hidden + setIsTimerRunning(false); + setTimeRemaining(0); + } + }, [ + showSequence, + currentSequenceIndex, + sequences, + showFeedback, + currentGameLevel, + ]); + + const handleLetterClick = (letter) => { + if (showSequence) return; // Don't allow input while showing sequence + + setUserInput((prev) => [...prev, letter]); + }; + + const handleRemoveLast = () => { + setUserInput((prev) => prev.slice(0, -1)); + }; + + const handleCheckSequence = () => { + if (!sequences[currentSequenceIndex]) { + console.warn( + "Memory Challenge - No sequence at index:", + currentSequenceIndex + ); + return; + } + + const currentSequence = sequences[currentSequenceIndex]; + const isCorrectAnswer = + JSON.stringify(userInput) === JSON.stringify(currentSequence.sequence); + + // Track question summary for assessment + const questionStartTime = levelStartTime || Date.now(); + const responseTime = Date.now() - (questionStartTime || Date.now()); + + const newSummary = { + questionId: `sequence_${currentSequenceIndex + 1}`, + questionType: "memoryChallenge", + isCorrect: isCorrectAnswer, + userAnswer: userInput.join(""), + correctAnswer: currentSequence.sequence.join(""), + responseTime, + }; + + // ✅ compute updated summaries synchronously + const updatedSummaries = [...questionSummaries, newSummary]; + + setQuestionSummaries(updatedSummaries); + setIsCorrect(isCorrectAnswer); + setShowFeedback(true); + + if (isCorrectAnswer) { + setCorrectCount((prev) => prev + 1); + } else { + setWrongCount((prev) => prev + 1); + } + + setCompletedSequences((prev) => prev + 1); + + setTimeout(() => { + const isLastSequence = currentSequenceIndex >= sequences.length - 1; + + if (isLastSequence) { + // ✅ pass summaries forward + handleLevelComplete(updatedSummaries); + } else { + setCurrentSequenceIndex((prev) => prev + 1); + setShowSequence(true); + setUserInput([]); + setShowFeedback(false); + setShowTimeoutMessage(false); + } + }, 1500); + }; + + const handleLevelComplete = async (questionSummariesParam) => { + const questionSummaries = questionSummariesParam || []; + // Check pass criteria: >= 80% accuracy + // Use Math.round to avoid floating point precision issues + const totalQuestions = questionSummaries.length; + const actualCorrect = questionSummaries.filter((q) => q.isCorrect).length; + + const accuracy = + totalQuestions > 0 + ? Math.round((actualCorrect / totalQuestions) * 100) + : 0; + + console.log( + `Memory Challenge - Level ${currentGameLevel} completed. Accuracy: ${accuracy}% (${correctCount}/${sequences.length} correct)` + ); + + // Calculate total time spent + const timeSpent = levelStartTime + ? Math.round((Date.now() - levelStartTime) / 1000) + : 0; + setTotalTimeSpent((prev) => prev + timeSpent); + + // Call assessment API for both pass and fail + const currentUser = sessionManager.getCurrentUser(); + if (currentUser && questionSummaries.length > 0) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + // Use sessionId prop if provided, otherwise fallback to telemetry sessionId + const effectiveSessionId = sessionId || currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + const actualCorrect = questionSummaries.filter((q) => q.isCorrect).length; + + try { + await trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: `memoryChallenge_${initialLanguage}`, + gameTitle: "Memory Challenge", + level: currentGameLevel, + language: initialLanguage, + totalQuestions: questionSummaries.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: timeSpent, + assessmentSummary: questionSummaries, + sessionId: effectiveSessionId, + subsessionId: subsessionId, + sub_session_id: assessmentParams.sub_session_id, + sub_milestone_level: assessmentParams.sub_milestone_level, + apply_level: assessmentParams.apply_level, + sub_apply_level: isShowCase ? currentGameLevel : undefined, + metadata: { + difficulty: "simple", + levelFailed: accuracy < 80, + scorePercentage: accuracy, + }, + }); + console.log( + "Memory Challenge assessment tracking created for level:", + currentGameLevel + ); + } catch (error) { + console.error("Error creating assessment tracking:", error); + } + } + + if (accuracy >= 80) { + // Level passed - show success screen + console.log( + `Memory Challenge - Level ${currentGameLevel} passed (accuracy: ${accuracy}%)` + ); + setIsGameComplete(true); + setLevelPassed(true); + setLevelFailed(false); + } else { + // Level failed - redirect based on failRedirect + // A1: Memory Challenge failure → P1 (failRedirect) + // A2: Memory Challenge failure → P6 (failRedirect) + console.log( + `Memory Challenge - Level ${currentGameLevel} failed (accuracy: ${accuracy}%, need >= 80%), redirecting to ${ + failRedirect || "P1" + }` + ); + setIsGameComplete(true); + setLevelPassed(false); + setLevelFailed(true); + + // For Apply steps with failRedirect, automatically redirect + // A1: redirects to P1, A2: redirects to P6 + if (isShowCase && failRedirect && isF3FlowActive) { + // Store redirect info for Practice.jsx to handle + setLocalData("f3FlowRedirect", failRedirect); + // Clear f3ApplySubStep to ensure Apply step starts from Letter Launcher when reached again + setLocalData("f3ApplySubStep", null); + // Redirect after a short delay to show failure state + setTimeout(() => { + if (handleNext) { + handleNext(); + } + }, 2000); + } + } + }; + + const resetGame = () => { + // IMPORTANT: Only redirect to failRedirect if level actually failed + // Do NOT redirect if all levels are complete (should use passRedirect instead) + const isAllLevelsComplete = currentGameLevel >= endLevel; + + // For Apply steps with failRedirect, redirect to Practice step when level fails + // Check if redirect flag exists in localStorage (set when level failed) + const redirectFlag = getLocalData("f3FlowRedirect"); + const shouldRedirect = + isShowCase && + failRedirect && + isF3FlowActive && + !isAllLevelsComplete && // IMPORTANT: Don't redirect if all levels are complete + (levelFailed || redirectFlag === failRedirect); + + if (shouldRedirect) { + console.log( + `Memory Challenge - Level ${currentGameLevel} failed in Apply step, redirecting to ${failRedirect}` + ); + // Clear failure flags + setLevelFailed(false); + // Ensure redirect flag is set + setLocalData("f3FlowRedirect", failRedirect); + // Clear f3ApplySubStep to ensure A1 starts from Letter Launcher when reached again + setLocalData("f3ApplySubStep", null); + // Reset state first + setIsGameComplete(false); + setLevelPassed(false); + // Then redirect by calling handleNext + if (handleNext) { + handleNext(); + return; + } + } + + // Reset game state for retry (same level) + setIsGameComplete(false); + setLevelPassed(false); + setLevelFailed(false); + setCurrentSequenceIndex(0); + setCorrectCount(0); + setWrongCount(0); + setCompletedSequences(0); // Reset completed sequences count + setUserInput([]); + setShowSequence(true); + setShowFeedback(false); + setShowTimeoutMessage(false); + setIsTimerRunning(false); + setTimeRemaining(0); + const newSequences = generateSequences(); + setSequences(newSequences); + }; + + if (!sessionInitialized || sequences.length === 0) { + return ( + +
+

Loading game...

+
+
+ ); + } + + // Show completion screen when game is complete + if (isGameComplete) { + const totalSequences = sequences.length; + const totalCorrect = correctCount; + // Use Math.round to avoid floating point precision issues + const accuracy = + totalSequences > 0 + ? Math.round((totalCorrect / totalSequences) * 100) + : 0; + + // Calculate stars based on accuracy + const starsEarned = + accuracy >= 100 ? 3 : accuracy >= 90 ? 2 : accuracy >= 80 ? 1 : 0; + + // Debug logging + console.log("Memory Challenge - Completion Screen:", { + totalCorrect, + totalSequences, + accuracy, + levelFailed, + levelPassed, + starsEarned, + }); + + // Override levelFailed if accuracy is actually >= 80% (should pass) + // This handles edge cases where state might be incorrect + const shouldPass = accuracy >= 80; + const actuallyFailed = levelFailed && !shouldPass; + + // Show failure screen only if actually failed (accuracy < 80%) + if (actuallyFailed) { + // For Apply steps, ensure redirect flag is set + if (isShowCase && failRedirect && isF3FlowActive) { + setLocalData("f3FlowRedirect", failRedirect); + console.log( + `Memory Challenge - Failure screen shown, redirect flag set to ${failRedirect}` + ); + } + + return ( + +
+ +
+
+ ); + } + + // Show success screen if level passed OR accuracy >= 80% + if (levelPassed || shouldPass) { + const hasMoreLevels = currentGameLevel < endLevel; + // IMPORTANT: Check if all levels are complete + // When currentGameLevel equals endLevel, we've completed all levels + // For example: if endLevel is 3, and currentGameLevel is 3, all levels are complete + const isAllLevelsComplete = currentGameLevel >= endLevel; + + console.log("Memory Challenge - Success screen check:", { + currentGameLevel, + endLevel, + hasMoreLevels, + isAllLevelsComplete, + passRedirect, + failRedirect, + }); + + return ( + +
+ { + // Clear completion state + setIsGameComplete(false); + setLevelPassed(false); + setLevelFailed(false); + + if (hasMoreLevels) { + // Move to next level + console.log( + `Memory Challenge - Level ${currentGameLevel} passed, moving to Level ${ + currentGameLevel + 1 + }` + ); + setCurrentGameLevel((prev) => prev + 1); + setCurrentSequenceIndex(0); + setCorrectCount(0); + setWrongCount(0); + setCompletedSequences(0); // Reset completed sequences count for new level + setUserInput([]); + setShowSequence(true); + setShowFeedback(false); + setShowTimeoutMessage(false); + setIsTimerRunning(false); + setTimeRemaining(0); + const newSequences = generateSequences(); + setSequences(newSequences); + } else if (isAllLevelsComplete) { + // All levels passed - redirect based on passRedirect + console.log( + `Memory Challenge - All ${endLevel} levels passed, redirecting to ${passRedirect}` + ); + + // For A2, if passRedirect is "complete", redirect directly to discovery start + if ( + isF3FlowActive && + passRedirect === "complete" && + applyStep === 2 + ) { + console.log( + "Memory Challenge - A2 completed successfully - F3 flow complete, redirecting to discover-start" + ); + + const lang = getLocalData("lang") || "en"; + const sessionId = getLocalData("sessionId"); + + try { + await addLesson({ + sessionId: sessionId, + milestone: "practice", + lesson: "0", + progress: 0, + language: lang, + milestoneLevel: "m1", + }); + console.log("F3 A2 completion - M1 lesson added"); + } catch (e) { + console.error( + "Error adding M1 lesson after F3 A2 completion:", + e + ); + } + + // Clear F3 flow data + setLocalData("f3FlowIndex", null); + setLocalData("f3FlowComplete", "true"); + setLocalData("f3ApplySubStep", null); + // Clear practice progress + setLocalData("practiceProgress", null); + // Redirect to discover-start + navigate("/discover-start"); + return; + } + + // For A1 or other cases, use passRedirect (e.g., P6 for A1) + if (isF3FlowActive && passRedirect) { + // IMPORTANT: Clear any previous failRedirect flag and set passRedirect + // This ensures we redirect to P6 (passRedirect) not P1 (failRedirect) + const existingRedirect = getLocalData("f3FlowRedirect"); + console.log( + `Memory Challenge - Clearing existing redirect (${existingRedirect}) and setting passRedirect to ${passRedirect}` + ); + // Clear f3ApplySubStep to ensure clean transition + setLocalData("f3ApplySubStep", null); + // Set passRedirect (P6 for A1) + setLocalData("f3FlowRedirect", passRedirect); + } + if (handleNext) { + handleNext(); + } + } + }} + continueButtonText={ + hasMoreLevels + ? "Next Level" + : isAllLevelsComplete + ? "Continue" + : "Play Again" + } + /> +
+
+ ); + } + } + + const currentSequence = sequences[currentSequenceIndex]; + + // Calculate progress and score + // Progress should show completed sequences, not current sequence index + const progress = completedSequences; + const totalSequences = sequences.length; + const score = correctCount; + + return ( + +
+ + +
+ {/* Header */} +
+ + +
+

+ Memory Challenge +

+
+ + + Level {currentGameLevel} / {endLevel || 3} + +
+
+ + {/* Spacer to balance layout */} +
+
+ + {/* Main Content Card */} +
+ {/* Progress Bar */} +
+
+ + Progress: {progress}/{totalSequences} + +
+
+ + + {score} + +
+ {/* Timer - Always reserve space to maintain consistent height */} +
+ {showSequence && !showTimeoutMessage && ( +
+ +
+ )} +
+
+
+
+
+
+
+ + {/* Time Up Button - Show when sequence display time expires */} + {/* Always reserve space to maintain consistent height between phases */} +
+ {showTimeoutMessage && ( +
+
+ +
+ Time Up! +
+
+
+ )} +
+ + {/* Game Area */} +
+ {currentSequence && ( + { + // This is handled by handleCheckSequence's setTimeout + // No need to do anything here to avoid conflicts + }} + showContinueButton={false} + /> + )} +
+
+
+ + +
+ + ); +}; + +const MemoryChallengeMechanics = (props) => { + return ; +}; + +export default MemoryChallengeMechanics; diff --git a/src/components/Practice/ParagraphFlow.jsx b/src/components/Practice/ParagraphFlow.jsx index 3aec649e..18ebe10a 100644 --- a/src/components/Practice/ParagraphFlow.jsx +++ b/src/components/Practice/ParagraphFlow.jsx @@ -23,12 +23,12 @@ import { getLocalData, setLocalData, } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import SpeechRecognition, { useSpeechRecognition, } from "react-speech-recognition"; import { Modal } from "@mui/material"; -import ZoomInIcon from "@mui/icons-material/ZoomIn"; -import CloseIcon from "@mui/icons-material/Close"; +import ZoomableImage from "./ZoomableImage"; const paragraphPages = [ { @@ -192,11 +192,27 @@ const ParagraphFlow = ({ const [finalTranscript, setFinalTranscript] = useState(""); const [isMatch, setIsMatch] = useState(false); const [open, setOpen] = useState(false); - const [zoomOpen, setZoomOpen] = useState(false); const correctPracticeWords = getLocalData("correctPracticeWords"); const sessionId = getLocalData("sessionId"); console.log("audios", parentWords); + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + const paragraphPages = [ { page: 1, @@ -206,7 +222,7 @@ const ParagraphFlow = ({ word, audio: `${ process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL - }/multilingual_audios/${data?.kn?.audio_url || ""}`, + }/multilingual_audios/${data?.[multilingualLangCode]?.audio_url || ""}`, })), }, ]; @@ -561,9 +577,14 @@ const ParagraphFlow = ({ currentPracticeStep = progressDatas?.currentPracticeStep; } - const currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; + let currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; let apiLevel = `M${level}-${currentLevel}`; + if (level >= 4 && level <= 9) { + currentLevel = practiceSteps?.[currentPracticeStep]?.name; + apiLevel = `M${level}-${currentLevel}`; + } + const callTelemetry = async () => { const sessionId = getLocalData("sessionId"); const responseStartTime = new Date().getTime(); @@ -1178,112 +1199,16 @@ const ParagraphFlow = ({ marginTop: "8px", }} /> */} - - contentImage setZoomOpen(true)} // Open modal on click - /> - - {/* Subtle gradient overlay across the top */} - - {/* Zoom icon positioned in the top-left corner */} - setZoomOpen(true)} - sx={{ color: "white", fontSize: "22px", cursor: "pointer" }} - /> - - - setZoomOpen(false)} - sx={{ - display: "flex", - alignItems: "center", - justifyContent: "center", - zIndex: 99999999, + - - {/* Subtle gradient overlay at the top of the zoomed image */} - - {/* Close icon positioned within the gradient overlay */} - setZoomOpen(false)} - sx={{ - color: "white", - fontSize: "24px", - cursor: "pointer", - backgroundColor: "rgba(0, 0, 0, 0.5)", - borderRadius: "50%", - padding: "4px", - }} - /> - - - Zoomed content - - + />
{/* Listen Bear - BOTTOM LEFT OF BOOK with larger size */} @@ -1402,7 +1327,8 @@ const ParagraphFlow = ({ >

{ + const nativeLang = getLocalData("nativeLang"); + const langSymbolMap = { + ka: "ಕ", // Kannada (from LanguageModal) + kn: "ಕ", // Kannada (from AllLanguages) + tn: "இ", // Tamil (from AllLanguages, using "ta" symbol) + ta: "இ", // Tamil (from AllLanguages) + te: "ఈ", // Telugu (from AllLanguages) + hi: "क", // Hindi (from AllLanguages) + gu: "ક", // Gujarati (from AllLanguages) + or: "କ", // Odia (from AllLanguages) + }; + return langSymbolMap[nativeLang] || "ಕ"; // Default to Kannada if not found + }; + const nativeLangSymbol = getNativeLangSymbol(); + + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); const { transcript, interimTranscript, @@ -104,34 +138,84 @@ const PhrasesInAction = ({ browserSupportsSpeechRecognition, } = useSpeechRecognition(); - function convertToStepFormat(rawData, audioFile) { - if (!rawData || !rawData.options) return {}; + function convertToStepFormat(rawData, audioFile, contentSourceData) { + // If rawData has options (mechanics_data format), use it + if (rawData && rawData.options && rawData.options.length > 0) { + const correctOption = rawData.options.find((opt) => opt.isAns); - const correctOption = rawData.options.find((opt) => opt.isAns); + const step1 = { + allwords: [ + { + img: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/${correctOption.image_url}`, + text: correctOption.text, + }, + ], + audio: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${audioFile}`, + }; - const step1 = { - allwords: [ - { - img: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/${correctOption.image_url}`, - text: correctOption.text, - }, - ], - audio: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${audioFile}`, - }; + const step2 = { + allwordsTwo: rawData.options.map((opt) => ({ + img: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/${opt.image_url}`, + text: opt.text, + })), + correctWordTwo: correctOption.text, + audio: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${audioFile}`, + }; - const step2 = { - allwordsTwo: rawData.options.map((opt) => ({ - img: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/${opt.image_url}`, - text: opt.text, - })), - correctWordTwo: correctOption.text, - audio: `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${audioFile}`, - }; + return { step1, step2 }; + } + + // Fallback: If contentSourceData is available (simple template format) + if (contentSourceData && contentSourceData.text) { + const imageUrl = + contentSourceData.image_url || contentSourceData.imageUrl; + const audioUrl = + audioFile || contentSourceData.audio_url || contentSourceData.audioUrl; + const text = contentSourceData.text; + + const step1 = { + allwords: [ + { + img: imageUrl + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/${imageUrl}` + : contentSourceData.image || "", + text: text, + }, + ], + audio: audioUrl + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${audioUrl}` + : contentSourceData.audio || "", + }; + + // For step2, we'll use the same content (can be enhanced later if needed) + const step2 = { + allwordsTwo: [ + { + img: imageUrl + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/${imageUrl}` + : contentSourceData.image || "", + text: text, + }, + ], + correctWordTwo: text, + audio: audioUrl + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/${audioUrl}` + : contentSourceData.audio || "", + }; + + return { step1, step2 }; + } - return { step1, step2 }; + return {}; } - let levelDataRaw = convertToStepFormat(parentWords, currentImg?.audioUrl); + let levelDataRaw = convertToStepFormat( + parentWords, + currentImg?.audioUrl, + currentImg + ); + + // Note: currentSteps is declared later, so we can't use it here yet const mediaRecorderRef = useRef(null); const recordedChunksRef = useRef([]); @@ -140,7 +224,6 @@ const PhrasesInAction = ({ const transcriptRef = useRef(""); useEffect(() => { transcriptRef.current = transcript; - //console.log("Live Transcript:", transcript); // Only check if there's new content and we're not already in abusive state if (transcript && !abusiveFound) { @@ -197,7 +280,6 @@ const PhrasesInAction = ({ mediaRecorder.onstop = () => { if (recordedChunksRef.current.length === 0) { - console.warn("No audio data captured."); setRecordedBlob(null); return; } @@ -252,7 +334,7 @@ const PhrasesInAction = ({ currentPracticeStep = progressDatas?.currentPracticeStep; } - let currentLevel = practiceSteps?.[currentPracticeStep]?.titleThree || "L1"; + let currentLevel = practiceSteps?.[currentPracticeStep]?.titleNew || "L1"; let apiLevel = `M${level}-${currentLevel}`; @@ -264,19 +346,669 @@ const PhrasesInAction = ({ currentLevel = practiceSteps?.[currentPracticeStep]?.name; } - const getInitialStep = (level) => { - return level === "L1" || level === "L3" ? "step1" : "step2"; + const getInitialStep = (stepLevel, milestoneLevel) => { + // For M3 (milestoneLevel 3), determine step based on the new structure: + // Phrase Reading/Repeat Phrase (step1): L1, L2, L3, P1, P3, P4, S1, S2 + // Correct Image Phrase (step2): P2, L4 + if (String(milestoneLevel) === "3") { + // For M3, use step1 for most steps, step2 only for P2 and L4 + const isStep2 = stepLevel === "L2" || stepLevel === "L4"; + return isStep2 ? "step2" : "step1"; + } + // For other levels, use original logic + return stepLevel === "L1" || stepLevel === "L3" ? "step1" : "step2"; }; const [currentSteps, setCurrentSteps] = useState( - getInitialStep(currentLevel) + getInitialStep(currentLevel, level) ); - //console.log("m3", currentLevel, level); + // Note: levelData is computed later + + const levelContent = { + length: 10, + en: { + L1: [ + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.sunShinesImg) || Assets.sunShinesImg, + text: "The Sun Shines", + }, + ], + audio: + getAssetAudioUrl(s3Assets.sunShinesAudio) || + Assets.sunShinesAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, + text: "We Play", + }, + { + img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, + text: "He Dances", + }, + { + img: getAssetUrl(s3Assets.sunShinesImg) || Assets.sunShinesImg, + text: "The Sun Shines", + }, + ], + correctWordTwo: "The Sun Shines", + audio: + getAssetAudioUrl(s3Assets.sunShinesAudio) || + Assets.sunShinesAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.fishSwimImg) || Assets.fishSwimImg, + text: "Fish Swims", + }, + ], + audio: + getAssetAudioUrl(s3Assets.fishSwimAudio) || Assets.fishSwimAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + { + img: getAssetUrl(s3Assets.fishSwimImg) || Assets.fishSwimImg, + text: "Fish Swims", + }, + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + ], + correctWordTwo: "Fish Swims", + audio: + getAssetAudioUrl(s3Assets.fishSwimAudio) || Assets.fishSwimAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", + }, + ], + audio: + getAssetAudioUrl(s3Assets.birdsFlyAudio) || Assets.birdsFlyAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.sheReadsImg) || Assets.sheReadsImg, + text: "She Reads", + }, + { + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", + }, + { + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", + }, + ], + correctWordTwo: "Birds Fly", + audio: + getAssetAudioUrl(s3Assets.birdsFlyAudio) || Assets.birdsFlyAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.sheSmilesImg) || Assets.sheSmilesImg, + text: "She Smiles", + }, + ], + audio: + getAssetAudioUrl(s3Assets.sheSmilesAudio) || + Assets.sheSmilesAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.sheSmilesImg) || Assets.sheSmilesImg, + text: "She Smiles", + }, + { + img: getAssetUrl(s3Assets.babyCriesImg) || Assets.babyCriesImg, + text: "Baby Cries", + }, + { + img: getAssetUrl(s3Assets.heEatsImg) || Assets.heEatsImg, + text: "He Eats", + }, + ], + correctWordTwo: "She Smiles", + audio: + getAssetAudioUrl(s3Assets.sheSmilesAudio) || + Assets.sheSmilesAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.theyLaughImg) || Assets.theyLaughImg, + text: "They Laugh", + }, + ], + audio: + getAssetAudioUrl(s3Assets.theyLaughAudio) || + Assets.theyLaughAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.youCookImg) || Assets.youCookImg, + text: "You Cook", + }, + { + img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, + text: "We Play", + }, + { + img: getAssetUrl(s3Assets.theyLaughImg) || Assets.theyLaughImg, + text: "They Laugh", + }, + ], + correctWordTwo: "They Laugh", + audio: + getAssetAudioUrl(s3Assets.theyLaughAudio) || + Assets.theyLaughAudio, + }, + }, + ], + + L2: [ + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, + text: "We Play", + }, + ], + audio: getAssetAudioUrl(s3Assets.wePlayAudio) || Assets.wePlayAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.sunShinesImg) || Assets.sunShinesImg, + text: "Sun Shines", + }, + { + img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, + text: "We Play", + }, + { + img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, + text: "He Dances", + }, + ], + correctWordTwo: "We Play", + audio: getAssetAudioUrl(s3Assets.wePlayAudio) || Assets.wePlayAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, + text: "He Dances", + }, + ], + audio: + getAssetAudioUrl(s3Assets.heDancesAudio) || Assets.heDancesAudio, + }, + step2: { + allwordsTwo: [ + { + img: + getAssetUrl(s3Assets.clocksTickImg) || Assets.clocksTickImg, + text: "Clocks Tick", + }, + { + img: getAssetUrl(s3Assets.sheSingsImg) || Assets.sheSingsImg, + text: "She Sings", + }, + { + img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, + text: "He Dances", + }, + ], + correctWordTwo: "He Dances", + audio: + getAssetAudioUrl(s3Assets.heDancesAudio) || Assets.heDancesAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.sheSingsImg) || Assets.sheSingsImg, + text: "She Sings", + }, + ], + audio: + getAssetAudioUrl(s3Assets.sheSingsAudio) || Assets.sheSingsAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.sheSingsImg) || Assets.sheSingsImg, + text: "She Sings", + }, + { + img: + getAssetUrl(s3Assets.flowersBloomImg) || + Assets.flowersBloomImg, + text: "Flowers Bloom", + }, + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + ], + correctWordTwo: "She Sings", + audio: + getAssetAudioUrl(s3Assets.sheSingsAudio) || Assets.sheSingsAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + ], + audio: + getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", + }, + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + { + img: getAssetUrl(s3Assets.babyCriesImg) || Assets.babyCriesImg, + text: "Baby Cries", + }, + ], + correctWordTwo: "Dogs Bark", + audio: + getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + ], + audio: + getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + { + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", + }, + { + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", + }, + ], + correctWordTwo: "It Rains", + audio: + getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, + }, + }, + ], + + P1: [ + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.sunShinesImg) || Assets.sunShinesImg, + text: "The Sun Shines", + }, + ], + audio: + getAssetAudioUrl(s3Assets.sunShinesAudio) || + Assets.sunShinesAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, + text: "We Play", + }, + { + img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, + text: "He Dances", + }, + { + img: getAssetUrl(s3Assets.sunShinesImg) || Assets.sunShinesImg, + text: "The Sun Shines", + }, + ], + correctWordTwo: "The Sun Shines", + audio: + getAssetAudioUrl(s3Assets.sunShinesAudio) || + Assets.sunShinesAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.fishSwimImg) || Assets.fishSwimImg, + text: "Fish Swims", + }, + ], + audio: + getAssetAudioUrl(s3Assets.fishSwimAudio) || Assets.fishSwimAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + { + img: getAssetUrl(s3Assets.fishSwimImg) || Assets.fishSwimImg, + text: "Fish Swims", + }, + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + ], + correctWordTwo: "Fish Swims", + audio: + getAssetAudioUrl(s3Assets.fishSwimAudio) || Assets.fishSwimAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", + }, + ], + audio: + getAssetAudioUrl(s3Assets.birdsFlyAudio) || Assets.birdsFlyAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.sheReadsImg) || Assets.sheReadsImg, + text: "She Reads", + }, + { + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", + }, + { + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", + }, + ], + correctWordTwo: "Birds Fly", + audio: + getAssetAudioUrl(s3Assets.birdsFlyAudio) || Assets.birdsFlyAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.sheSmilesImg) || Assets.sheSmilesImg, + text: "She Smiles", + }, + ], + audio: + getAssetAudioUrl(s3Assets.sheSmilesAudio) || + Assets.sheSmilesAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.sheSmilesImg) || Assets.sheSmilesImg, + text: "She Smiles", + }, + { + img: getAssetUrl(s3Assets.babyCriesImg) || Assets.babyCriesImg, + text: "Baby Cries", + }, + { + img: getAssetUrl(s3Assets.heEatsImg) || Assets.heEatsImg, + text: "He Eats", + }, + ], + correctWordTwo: "She Smiles", + audio: + getAssetAudioUrl(s3Assets.sheSmilesAudio) || + Assets.sheSmilesAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.theyLaughImg) || Assets.theyLaughImg, + text: "They Laugh", + }, + ], + audio: + getAssetAudioUrl(s3Assets.theyLaughAudio) || + Assets.theyLaughAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.youCookImg) || Assets.youCookImg, + text: "You Cook", + }, + { + img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, + text: "We Play", + }, + { + img: getAssetUrl(s3Assets.theyLaughImg) || Assets.theyLaughImg, + text: "They Laugh", + }, + ], + correctWordTwo: "They Laugh", + audio: + getAssetAudioUrl(s3Assets.theyLaughAudio) || + Assets.theyLaughAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + ], + audio: + getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", + }, + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + { + img: getAssetUrl(s3Assets.babyCriesImg) || Assets.babyCriesImg, + text: "Baby Cries", + }, + ], + correctWordTwo: "Dogs Bark", + audio: + getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + ], + audio: + getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", + }, + { + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", + }, + { + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", + }, + ], + correctWordTwo: "It Rains", + audio: + getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.youSwimImg) || Assets.youSwimImg, + text: "You Swim", + }, + ], + audio: + getAssetAudioUrl(s3Assets.youSwimAudio) || Assets.youSwimAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.heEatsImg) || Assets.heEatsImg, + text: "He Eats", + }, + { + img: getAssetUrl(s3Assets.sheReadsImg) || Assets.sheReadsImg, + text: "She Reads", + }, + { + img: getAssetUrl(s3Assets.youSwimImg) || Assets.youSwimImg, + text: "You Swim", + }, + ], + correctWordTwo: "You Swim", + audio: + getAssetAudioUrl(s3Assets.youSwimAudio) || Assets.youSwimAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", + }, + ], + audio: getAssetAudioUrl(s3Assets.iSleepAudio) || Assets.iSleepAudio, + }, + step2: { + allwordsTwo: [ + { + img: + getAssetUrl(s3Assets.flowersBloomImg) || + Assets.flowersBloomImg, + text: "Flowers Bloom", + }, + { + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", + }, + { + img: getAssetUrl(s3Assets.fireBurnsImg) || Assets.fireBurnsImg, + text: "Fire Burns", + }, + ], + correctWordTwo: "I Sleep", + audio: getAssetAudioUrl(s3Assets.iSleepAudio) || Assets.iSleepAudio, + }, + }, + { + step1: { + allwords: [ + { + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", + }, + ], + audio: getAssetAudioUrl(s3Assets.weWinAudio) || Assets.weWinAudio, + }, + step2: { + allwordsTwo: [ + { + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", + }, + { + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", + }, + { + img: getAssetUrl(s3Assets.sheReadsImg) || Assets.sheReadsImg, + text: "She Reads", + }, + ], + correctWordTwo: "We Win", + audio: getAssetAudioUrl(s3Assets.weWinAudio) || Assets.weWinAudio, + }, + }, + ], - const levelContent = { - en: { - L1: [ + P2: [ { step1: { allwords: [ @@ -438,163 +1170,157 @@ const PhrasesInAction = ({ Assets.theyLaughAudio, }, }, - ], - - L2: [ { step1: { allwords: [ { - img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, - text: "We Play", + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", }, ], - audio: getAssetAudioUrl(s3Assets.wePlayAudio) || Assets.wePlayAudio, + audio: + getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, }, step2: { allwordsTwo: [ { - img: getAssetUrl(s3Assets.sunShinesImg) || Assets.sunShinesImg, - text: "Sun Shines", + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", }, { - img: getAssetUrl(s3Assets.wePlayImg) || Assets.wePlayImg, - text: "We Play", + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", }, { - img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, - text: "He Dances", + img: getAssetUrl(s3Assets.babyCriesImg) || Assets.babyCriesImg, + text: "Baby Cries", }, ], - correctWordTwo: "We Play", - audio: getAssetAudioUrl(s3Assets.wePlayAudio) || Assets.wePlayAudio, + correctWordTwo: "Dogs Bark", + audio: + getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, }, }, { step1: { allwords: [ { - img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, - text: "He Dances", + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", }, ], audio: - getAssetAudioUrl(s3Assets.heDancesAudio) || Assets.heDancesAudio, + getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, }, step2: { allwordsTwo: [ { - img: - getAssetUrl(s3Assets.clocksTickImg) || Assets.clocksTickImg, - text: "Clocks Tick", + img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, + text: "It Rains", }, { - img: getAssetUrl(s3Assets.sheSingsImg) || Assets.sheSingsImg, - text: "She Sings", + img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, + text: "Birds Fly", }, { - img: getAssetUrl(s3Assets.heDancesImg) || Assets.heDancesImg, - text: "He Dances", + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", }, ], - correctWordTwo: "He Dances", + correctWordTwo: "It Rains", audio: - getAssetAudioUrl(s3Assets.heDancesAudio) || Assets.heDancesAudio, + getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, }, }, { step1: { allwords: [ { - img: getAssetUrl(s3Assets.sheSingsImg) || Assets.sheSingsImg, - text: "She Sings", + img: getAssetUrl(s3Assets.youSwimImg) || Assets.youSwimImg, + text: "You Swim", }, ], audio: - getAssetAudioUrl(s3Assets.sheSingsAudio) || Assets.sheSingsAudio, + getAssetAudioUrl(s3Assets.youSwimAudio) || Assets.youSwimAudio, }, step2: { allwordsTwo: [ { - img: getAssetUrl(s3Assets.sheSingsImg) || Assets.sheSingsImg, - text: "She Sings", + img: getAssetUrl(s3Assets.heEatsImg) || Assets.heEatsImg, + text: "He Eats", }, { - img: - getAssetUrl(s3Assets.flowersBloomImg) || - Assets.flowersBloomImg, - text: "Flowers Bloom", + img: getAssetUrl(s3Assets.sheReadsImg) || Assets.sheReadsImg, + text: "She Reads", }, { - img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, - text: "It Rains", + img: getAssetUrl(s3Assets.youSwimImg) || Assets.youSwimImg, + text: "You Swim", }, ], - correctWordTwo: "She Sings", + correctWordTwo: "You Swim", audio: - getAssetAudioUrl(s3Assets.sheSingsAudio) || Assets.sheSingsAudio, + getAssetAudioUrl(s3Assets.youSwimAudio) || Assets.youSwimAudio, }, }, { step1: { allwords: [ { - img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, - text: "Dogs Bark", + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", }, ], - audio: - getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, + audio: getAssetAudioUrl(s3Assets.iSleepAudio) || Assets.iSleepAudio, }, step2: { allwordsTwo: [ { - img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, - text: "We Win", + img: + getAssetUrl(s3Assets.flowersBloomImg) || + Assets.flowersBloomImg, + text: "Flowers Bloom", }, { - img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, - text: "Dogs Bark", + img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, + text: "I Sleep", }, { - img: getAssetUrl(s3Assets.babyCriesImg) || Assets.babyCriesImg, - text: "Baby Cries", + img: getAssetUrl(s3Assets.fireBurnsImg) || Assets.fireBurnsImg, + text: "Fire Burns", }, ], - correctWordTwo: "Dogs Bark", - audio: - getAssetAudioUrl(s3Assets.dogsBarkAudio) || Assets.dogsBarkAudio, + correctWordTwo: "I Sleep", + audio: getAssetAudioUrl(s3Assets.iSleepAudio) || Assets.iSleepAudio, }, }, { step1: { allwords: [ { - img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, - text: "It Rains", + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", }, ], - audio: - getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, + audio: getAssetAudioUrl(s3Assets.weWinAudio) || Assets.weWinAudio, }, step2: { allwordsTwo: [ { - img: getAssetUrl(s3Assets.itRainsImg) || Assets.itRainsImg, - text: "It Rains", + img: getAssetUrl(s3Assets.weWinImg) || Assets.weWinImg, + text: "We Win", }, { - img: getAssetUrl(s3Assets.birdsFlyImg) || Assets.birdsFlyImg, - text: "Birds Fly", + img: getAssetUrl(s3Assets.dogsBarkImg) || Assets.dogsBarkImg, + text: "Dogs Bark", }, { - img: getAssetUrl(s3Assets.iSleepImg) || Assets.iSleepImg, - text: "I Sleep", + img: getAssetUrl(s3Assets.sheReadsImg) || Assets.sheReadsImg, + text: "She Reads", }, ], - correctWordTwo: "It Rains", - audio: - getAssetAudioUrl(s3Assets.itRainsAudio) || Assets.itRainsAudio, + correctWordTwo: "We Win", + audio: getAssetAudioUrl(s3Assets.weWinAudio) || Assets.weWinAudio, }, }, ], @@ -4911,19 +5637,43 @@ const PhrasesInAction = ({ //const levelData = content?.[currentLevel][currentWordIndex][currentSteps]; let levelData; + // First try to use API data (levelDataRaw) if (currentSteps === "step1") { levelData = levelDataRaw?.step1; } else { levelData = levelDataRaw?.step2; } - //console.log("dataP410", levelData, currentLevel); + // Fallback to hardcoded content if API data is not available + if ( + !levelData || + (currentSteps === "step1" && !levelData?.allwords) || + (currentSteps === "step2" && !levelData?.allwordsTwo) + ) { + const hardcodedContent = content?.[currentLevel]?.[currentWordIndex]; + if (hardcodedContent) { + if (currentSteps === "step1") { + levelData = hardcodedContent.step1; + } else { + levelData = hardcodedContent.step2; + } + } else { + console.error("PhrasesInAction - No hardcoded content found for:", { + currentLevel, + currentWordIndex, + availableKeys: Object.keys(content || {}), + contentForLevel: content?.[currentLevel] + ? `exists with ${content[currentLevel].length} items` + : "does not exist", + }); + } + } let audioElement = new Audio(levelData?.audio); useEffect(() => { //setStartGame(true); - setCurrentSteps(getInitialStep(currentLevel)); + setCurrentSteps(getInitialStep(currentLevel, level)); setCurrentWordIndex(0); setSelectedDiv(null); // Reset selection setIncorrectWords([]); // Clear incorrect words @@ -4994,8 +5744,48 @@ const PhrasesInAction = ({ } }; + const playRecordedAudio = () => { + if (!recordedBlob) { + return; + } + + // Stop any currently playing recorded audio + if (recordedAudioInstance) { + recordedAudioInstance.pause(); + recordedAudioInstance.currentTime = 0; + } + + const audioUrl = URL.createObjectURL(recordedBlob); + const audio = new Audio(audioUrl); + setRecordedAudioInstance(audio); + setIsPlayingRecordedAudio(true); + + audio.play().catch((error) => { + console.error("Error playing recorded audio:", error); + setIsPlayingRecordedAudio(false); + }); + + audio.onended = () => { + setIsPlayingRecordedAudio(false); + URL.revokeObjectURL(audioUrl); + }; + + audio.onerror = () => { + setIsPlayingRecordedAudio(false); + URL.revokeObjectURL(audioUrl); + }; + }; + + const stopRecordedAudio = () => { + if (recordedAudioInstance) { + recordedAudioInstance.pause(); + recordedAudioInstance.currentTime = 0; + setIsPlayingRecordedAudio(false); + } + }; + const goToNextStep = () => { - if (currentWordIndex < content[currentLevel]?.length - 1) { + if (currentWordIndex < levelContent["length"] - 1) { callTelemetry(); handleNext(); //setCurrentSteps(getInitialStep(currentLevel)); @@ -5012,8 +5802,26 @@ const PhrasesInAction = ({ setIsRecordingStopped2(false); // Reset second recording stop state setSelectedDiv2(null); } else { + // Last item completed - check if this is a showcase step (S1 or S2) callTelemetry(); - handleNext(); + + // Get current practice step info to determine if it's a showcase + let practiceProgress = getLocalData("practiceProgress"); + if (typeof practiceProgress === "string") { + practiceProgress = JSON.parse(practiceProgress); + } + const currentPracticeStep = practiceProgress?.currentPracticeStep; + const stepTitle = practiceSteps?.[currentPracticeStep]?.title; + const isShowcaseStep = stepTitle === "S1" || stepTitle === "S2"; + + // Only pass isGameOver=true for showcase steps (S1/S2) + // For regular steps (L1, P1, L2, etc.), just call handleNext() to continue to next step + if (isShowcaseStep) { + handleNext(true); // Pass true to indicate showcase completion + } else { + handleNext(); // Regular step completion - just continue to next step + } + setSelectedDiv(null); // Reset selection setIncorrectWords([]); // Clear incorrect words setIsCorrectImageSelected(false); // Reset selection status @@ -5037,6 +5845,8 @@ const PhrasesInAction = ({ const [isAnswerIncorrect, setIsAnswerIncorrect] = useState(false); const [audioInstance, setAudioInstance] = useState(null); const [open, setOpen] = useState(false); + const [isPlayingRecordedAudio, setIsPlayingRecordedAudio] = useState(false); + const [recordedAudioInstance, setRecordedAudioInstance] = useState(null); const handleMicClick2 = () => { if (!isRecording2) { @@ -5392,60 +6202,92 @@ const PhrasesInAction = ({ > {levelData?.allwords[0]?.text}

- - - {/* Kannada Letter Box */} - - ಕ - - + + {nativeLangSymbol} + + - -
-
+ +
+ + )}
+ {/* Listen to recorded audio button - step1, 2nd page, for all languages except English */}
- + {language !== "en" && recordedBlob && ( +
+ {isPlayingRecordedAudio ? ( + + ) : ( + + )} +
+ )} +
+ +
)} @@ -5459,40 +6301,63 @@ const PhrasesInAction = ({ margin: "20px 0", }} > - {levelData?.allwords?.map((item) => ( + {levelData?.allwords && levelData.allwords.length > 0 ? ( + levelData.allwords.map((item) => ( +
setSelectedDiv(item.text)} + > + {item.text} +
+ )) + ) : (
setSelectedDiv(item.text)} > - {item.text} +

+ ⚠️ No content available +

+

+ Debug: currentLevel={currentLevel}, currentSteps= + {currentSteps}, currentWordIndex={currentWordIndex} +

+

+ hasLevelData={String(!!levelData)}, hasAllwords= + {String(!!levelData?.allwords)}, allwordsLength= + {levelData?.allwords?.length || 0} +

- ))} + )}

)} @@ -5774,6 +6639,13 @@ const PhrasesInAction = ({ display: "flex", alignItems: "center", position: "relative", + marginTop: isMatched + ? isMobile + ? "30px" + : isTablet + ? "40px" + : "50px" + : "20px", }} > {isRecording2 && ( @@ -5876,60 +6748,92 @@ const PhrasesInAction = ({ > {levelData?.correctWordTwo}

- - - {/* Kannada Letter Box */} - - ಕ - - + + {nativeLangSymbol} + + - - - + + + + )}

+ {/* Listen to recorded audio button - step2, 2nd page, for all languages except English */}
- + {language !== "en" && recordedBlob && ( +
+ {isPlayingRecordedAudio ? ( + + ) : ( + + )} +
+ )} +
+ +
)} diff --git a/src/components/Practice/ReadMatch.jsx b/src/components/Practice/ReadMatch.jsx index 196627cd..c9a205a3 100644 --- a/src/components/Practice/ReadMatch.jsx +++ b/src/components/Practice/ReadMatch.jsx @@ -110,14 +110,35 @@ const ReadMatch = ({ try { const response = await getCorrectPracticeWords("false"); const correctWords = response?.data || []; + const selectedLang = getLocalData("lang") || "en"; console.log("level api", level); - const formattedCorrectWords = correctWords?.map((item) => ({ - word: item?.contentSourceData?.[0]?.text, - img: item?.imagePath || item?.mechanics_data?.[0]?.image_url, - match: item?.contentSourceData?.[0]?.text, - content_id: item?.contentId, - })); + // First, filter items by top-level language field to only include items matching selected language + const filteredWords = correctWords.filter((item) => { + // Check if item's top-level language matches + if (item?.language === selectedLang) { + return true; + } + // Also check if contentSourceData has an entry for the selected language + return item?.contentSourceData?.some( + (data) => data?.language === selectedLang + ); + }); + + const formattedCorrectWords = filteredWords?.map((item) => { + // Filter contentSourceData to find the entry matching the selected language + const contentData = + item?.contentSourceData?.find( + (data) => data?.language === selectedLang + ) || item?.contentSourceData?.[0]; // Fallback to first entry if language not found + + return { + word: contentData?.text, + img: item?.imagePath || item?.mechanics_data?.[0]?.image_url, + match: contentData?.text, + content_id: item?.contentId, + }; + }); if (formattedCorrectWords <= 3) { setLocalData("readMatch", false); diff --git a/src/components/Practice/RetryDialog.jsx b/src/components/Practice/RetryDialog.jsx new file mode 100644 index 00000000..9274f56a --- /dev/null +++ b/src/components/Practice/RetryDialog.jsx @@ -0,0 +1,164 @@ +import React from "react"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { useNavigate, useLocation } from "react-router-dom"; +import textureImage from "../../assets/images/textureImage.png"; +import cryPanda from "../../assets/images/cryPanda.svg"; +import { NextButton } from "../../utils/constants"; + +export const RetryDialog = ({ message, onRetry, onClose }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const navigate = useNavigate(); + const location = useLocation(); + + const handleRetry = () => { + // Call custom retry handler if provided (usually redirects to discover-start) + if (onRetry) { + onRetry(); + } else { + // Fallback: redirect to discover-start if no handler provided + navigate("/discover-start", { replace: true }); + } + }; + + const handleClose = () => { + if (onClose) { + onClose(); + } + }; + + return ( + + + + cryPanda + + + + + Oops... + + + + + + {message || "Something went wrong. Please try again."} + + + + + + + Retry + + + + + + + + + ); +}; + +export default RetryDialog; diff --git a/src/components/Practice/TowreFlow.jsx b/src/components/Practice/TowreFlow.jsx index 49852e46..b24409be 100644 --- a/src/components/Practice/TowreFlow.jsx +++ b/src/components/Practice/TowreFlow.jsx @@ -1,4 +1,11 @@ -import React, { useState, useEffect, useRef } from "react"; +import React, { + useState, + useEffect, + useRef, + memo, + useMemo, + useCallback, +} from "react"; import clockImg from "../../assets/clocck.svg"; import handImg from "../../assets/hand.svg"; import boxImg from "../../assets/box.svg"; @@ -12,7 +19,6 @@ import pandaTimerImg from "../../assets/pandaTimer1.svg"; import timerBoxImg from "../../assets/timerBox.svg"; import initialMessageBoxImg from "../../assets/initialMessageBox.svg"; import { doubleMetaphone } from "double-metaphone"; -import { pipeline, env } from "@xenova/transformers"; import { Box, useMediaQuery, createTheme } from "@mui/material"; import reportBoyImg from "../../assets/monkeyReport.svg"; import reportStarsandcloudsImg from "../../assets/starsandclouds.png"; @@ -22,18 +28,15 @@ import booksStackImg from "../../assets/totalWord.svg"; import reportPandaImg from "../../assets/pandaa.svg"; import reportImg from "../../assets/reportImg.svg"; import { setLocalData, getLocalData } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import { useNavigate, useLocation } from "react-router-dom"; import MainLayout from "../Layouts.jsx/MainLayout"; -import SpeechRecognition, { - useSpeechRecognition, -} from "react-speech-recognition"; +// Using native browser Speech Recognition API instead of library import { addTowreRecord } from "../../services/learnerAi/learnerAiService"; import * as Assets from "../../utils/imageAudioLinks"; import S3Client from "../../config/awsS3"; import { PutObjectCommand } from "@aws-sdk/client-s3"; -env.localModelPath = "https://huggingface.co/Xenova/whisper-tiny/resolve/main/"; - const allEnglishWords = [ { title: "is", isCorrect: false }, { title: "up", isCorrect: false }, @@ -212,7 +215,7 @@ const teluguWords = [ "దశాబ్దము", "ముత్తాత", "పిన్నీసు", - "శతబ్దం", + "శతాబ్దం", "స్కూటరు", "బొమ్మలు", "అత్తెము", @@ -1082,37 +1085,503 @@ const TowreFlow = ({ const [startTime, setStartTime] = useState(null); const [totalSec, setTotalSec] = useState(null); const [finalTranscript, setFinalTranscript] = useState(""); - const { - transcript, - interimTranscript, - listening, - resetTranscript, - browserSupportsSpeechRecognition, - } = useSpeechRecognition(); + const [transcript, setTranscript] = useState(""); + const [interimTranscript, setInterimTranscript] = useState(""); + const [listening, setListening] = useState(false); const lang = getLocalData("lang"); - const wordsByLang = { - en: allEnglishWords, - hi: allHindiWords, - te: allTeluguWords, - ka: allKannadaWords, - ta: allTamilWords, - }; + // Map language codes to browser Speech Recognition format + // Memoize to prevent function recreation on every render + const getBrowserLanguage = useCallback((langCode) => { + const browserLangMap = { + en: "en-US", + hi: "hi-IN", + te: "te-IN", + ka: "kn-IN", // Kannada + ta: "ta-IN", + }; + return browserLangMap[langCode] || "en-US"; + }, []); + + // Check browser support for native Speech Recognition API + const browserSupportsSpeechRecognition = useMemo(() => { + return !!(window.SpeechRecognition || window.webkitSpeechRecognition); + }, []); + + // Create recognition instance + const recognitionRef = useRef(null); + + // Initialize recognition instance + useEffect(() => { + if (browserSupportsSpeechRecognition && !recognitionRef.current) { + const SpeechRecognition = + window.SpeechRecognition || window.webkitSpeechRecognition; + recognitionRef.current = new SpeechRecognition(); + recognitionRef.current.continuous = true; + recognitionRef.current.interimResults = true; + recognitionRef.current.lang = getBrowserLanguage(lang); + + // Set up event handlers + recognitionRef.current.onstart = () => { + console.log("✅ Speech recognition started (onstart event)"); + setListening(true); + recognitionStartedRef.current = true; + }; + + recognitionRef.current.onresult = (event) => { + let interim = ""; + let final = ""; + + for (let i = event.resultIndex; i < event.results.length; i++) { + const transcript = event.results[i][0].transcript; + if (event.results[i].isFinal) { + final += transcript + " "; + } else { + interim += transcript; + } + } + + if (final) { + setTranscript((prev) => (prev + final).trim()); + setInterimTranscript(""); + console.log("✅ Final transcript received:", final.trim()); + } else if (interim) { + setInterimTranscript(interim); + console.log("📝 Interim transcript:", interim); + } + }; + + recognitionRef.current.onerror = (event) => { + console.error("❌ Speech recognition error:", event.error); + if (event.error === "aborted") { + console.log("ℹ️ Speech recognition aborted"); + } else if (event.error === "no-speech") { + console.log("ℹ️ No speech detected"); + } else if (event.error === "network") { + console.error( + "❌ Network error - speech recognition requires internet connection" + ); + } + setListening(false); + }; + + recognitionRef.current.onend = () => { + console.log("ℹ️ Speech recognition ended"); + setListening(false); + + // Auto-restart if we should be listening + // Use refs to avoid stale closure issues + if (shouldBeListeningRef.current) { + setTimeout(() => { + // Check current state via refs + if (shouldBeListeningRef.current && recognitionRef.current) { + try { + recognitionRef.current.start(); + console.log("🔄 Auto-restarting speech recognition"); + } catch (error) { + // Recognition might already be starting, ignore error + console.log( + "ℹ️ Recognition restart attempt (may already be starting):", + error.message + ); + } + } + }, 100); + } + }; + } - const allWords = wordsByLang[lang] || allEnglishWords; + // Cleanup on unmount + return () => { + if (recognitionRef.current) { + recognitionRef.current.onstart = null; + recognitionRef.current.onresult = null; + recognitionRef.current.onerror = null; + recognitionRef.current.onend = null; + try { + recognitionRef.current.stop(); + } catch (e) { + // Ignore errors during cleanup + } + recognitionRef.current = null; + } + }; + }, [browserSupportsSpeechRecognition, lang, getBrowserLanguage]); // Only depend on these - recognition instance should persist + + // Reset transcript function + const resetTranscript = useCallback(() => { + setTranscript(""); + setInterimTranscript(""); + transcriptRef.current = ""; + }, []); + + // Memoize language-based word selection to prevent recalculation on every render + const allWords = useMemo(() => { + const wordsByLang = { + en: allEnglishWords, + hi: allHindiWords, + te: allTeluguWords, + ka: allKannadaWords, + ta: allTamilWords, + }; + return wordsByLang[lang] || allEnglishWords; + }, [lang]); - const allWordSets = createWordSets(allWords); + // Memoize word sets creation - only recalculate when words or currentWordSetIndex changes + const allWordSets = useMemo(() => createWordSets(allWords), [allWords]); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); - const currentWordSet = allWordSets[currentWordSetIndex]; + const currentWordSet = useMemo( + () => allWordSets[currentWordSetIndex], + [allWordSets, currentWordSetIndex] + ); const transcriptRef = useRef(""); const location = useLocation(); background = "linear-gradient(45deg, #FF730E 30%, #FFB951 90%)"; showTimer = false; useEffect(() => { - transcriptRef.current = transcript; - //console.log("Live Transcript:", transcript); - }, [transcript]); + // Always log to see what's happening + if (listening) { + console.log( + `🎤 Listening - Transcript: "${transcript || ""}" | Interim: "${ + interimTranscript || "" + }" | Listening: ${listening}` + ); + + // Log if we have interim results but no final transcript + if (interimTranscript && !transcript) { + console.log("📝 Interim transcript available:", interimTranscript); + transcriptRef.current = interimTranscript; + } + + // Log if we have final transcript + if (transcript) { + console.log("✅ Final transcript received:", transcript); + transcriptRef.current = transcript; + } + + // If listening but no transcript at all, log this to help debug + if (!transcript && !interimTranscript) { + console.log( + "⚠️ Listening but no transcript yet - speak clearly into microphone" + ); + } + } else { + console.log( + `🔇 Not listening - Transcript: "${transcript || ""}" | Interim: "${ + interimTranscript || "" + }" | Listening: ${listening}` + ); + } + + // Combine transcript and interimTranscript for the most up-to-date value + const combinedTranscript = transcript || interimTranscript || ""; + if (combinedTranscript && combinedTranscript.trim().length > 0) { + transcriptRef.current = combinedTranscript; + } + }, [transcript, interimTranscript, listening]); + + // Track listening state changes and auto-restart if it stops unexpectedly + const shouldBeListeningRef = useRef(false); + const recognitionStartedRef = useRef(false); + const retryCountRef = useRef(0); + const maxRetries = 3; + const retryTimeoutRef = useRef(null); + const isRestartingRef = useRef(false); + const lastListeningStateRef = useRef(listening); + const restartDebounceRef = useRef(null); + + useEffect(() => { + // Only log if listening state actually changed + if (lastListeningStateRef.current !== listening) { + console.log("🎧 Speech recognition listening state changed:", listening); + lastListeningStateRef.current = listening; + + // If we just started listening and we expected it to start, verify it's working + if (listening && recognitionStartedRef.current) { + console.log( + "✅ Speech recognition confirmed active (listening state is true)" + ); + + // Check if transcript is updating after delays (helps verify the library is working) + const transcriptCheckTimeout1 = setTimeout(() => { + // Re-check current state in the transcript useEffect - it will log if still no transcript + console.log( + "⏱️ 3 seconds elapsed - checking if transcript is being received..." + ); + console.log("📊 Current state:", { + listening, + transcript: transcript || "(empty)", + interimTranscript: interimTranscript || "(empty)", + transcriptLength: transcript?.length || 0, + interimLength: interimTranscript?.length || 0, + }); + + if (listening && !transcript && !interimTranscript) { + console.warn( + "⚠️ Recognition is listening but no transcript received after 3 seconds." + ); + console.warn(" Possible causes:"); + console.warn( + " 1. Old handlers from hot-reload are interfering (do a hard refresh: Ctrl+Shift+R)" + ); + console.warn( + " 2. Microphone is not picking up audio - try speaking louder" + ); + console.warn(" 3. Browser speech recognition API issue"); + console.warn( + " 4. Network connectivity issue (speech recognition uses cloud service)" + ); + console.warn( + " 💡 Try speaking clearly into the microphone - the API may need more time" + ); + } + }, 3000); + + // Check again after 10 seconds to see if results come in later + const transcriptCheckTimeout2 = setTimeout(() => { + console.log("⏱️ 10 seconds elapsed - final check..."); + console.log("📊 Final state:", { + listening, + transcript: transcript || "(empty)", + interimTranscript: interimTranscript || "(empty)", + transcriptLength: transcript?.length || 0, + interimLength: interimTranscript?.length || 0, + }); + + if (listening && !transcript && !interimTranscript) { + console.error( + "❌ CRITICAL: Speech recognition is listening but NO transcript received after 10 seconds!" + ); + console.error( + " This indicates the Web Speech API is not returning results." + ); + console.error(" Solutions:"); + console.error( + " 1. Check internet connection (Web Speech API requires network)" + ); + console.error( + " 2. Try a different browser (Chrome/Edge work best)" + ); + console.error(" 3. Check browser console for network errors"); + console.error(" 4. Verify microphone is working in other apps"); + console.error(" 5. Try restarting the browser"); + } else if (transcript || interimTranscript) { + console.log( + "✅ Transcript received! The API is working, it just took longer than expected." + ); + } + }, 10000); + + // Cleanup timeouts on unmount or state change + return () => { + clearTimeout(transcriptCheckTimeout1); + clearTimeout(transcriptCheckTimeout2); + }; + } + } + + if (!listening && transcript) { + console.log( + "⚠️ Speech recognition stopped but transcript exists:", + transcript + ); + } + + // If we should be listening but we're not, try to restart with retry logic + // Add debounce to prevent rapid restart attempts during re-renders + if ( + shouldBeListeningRef.current && + !listening && + showFinalWords && + !showResults && + !isRestartingRef.current + ) { + // Clear any existing debounce timeout + if (restartDebounceRef.current) { + clearTimeout(restartDebounceRef.current); + } + + // Debounce restart attempts to avoid rapid-fire restarts during re-renders + restartDebounceRef.current = setTimeout(() => { + // Double-check conditions after debounce delay + if ( + shouldBeListeningRef.current && + !listening && + showFinalWords && + !showResults && + !isRestartingRef.current + ) { + isRestartingRef.current = true; + + // Clear any existing retry timeout + if (retryTimeoutRef.current) { + clearTimeout(retryTimeoutRef.current); + } + + const attemptRestart = (attemptNumber) => { + if ( + browserSupportsSpeechRecognition && + shouldBeListeningRef.current && + attemptNumber <= maxRetries + ) { + // Check listening state right before attempting restart + // If already listening, don't restart + if (listening) { + console.log( + "ℹ️ Recognition already listening, skipping restart" + ); + isRestartingRef.current = false; + retryCountRef.current = 0; + return; + } + + console.warn( + `⚠️ Speech recognition stopped unexpectedly. Attempting restart ${attemptNumber}/${maxRetries}...` + ); + try { + resetTranscript(); + if (recognitionRef.current) { + recognitionRef.current.lang = getBrowserLanguage(lang); + recognitionRef.current.start(); + console.log( + `🔄 Restart attempt ${attemptNumber} - command sent` + ); + } + retryCountRef.current = attemptNumber; + isRestartingRef.current = false; // Reset immediately, let useEffect handle next check + + // Don't check listening state here - it will be stale + // Let the useEffect cycle naturally check if it started + // If it didn't start, the useEffect will trigger again + } catch (error) { + console.error( + `❌ Error in restart attempt ${attemptNumber}:`, + error + ); + isRestartingRef.current = false; + if (attemptNumber < maxRetries) { + // Retry with exponential backoff: 1000ms, 2000ms, 4000ms + const delay = 1000 * Math.pow(2, attemptNumber - 1); + setTimeout(() => { + attemptRestart(attemptNumber + 1); + }, delay); + } else { + retryCountRef.current = 0; + isRestartingRef.current = false; + } + } + } + }; + + // Start retry sequence with delay + retryTimeoutRef.current = setTimeout(() => { + attemptRestart(1); + }, 1000); // Increased debounce delay to 1 second + } + }, 1000); // Debounce delay to prevent rapid restarts during re-renders + } + }, [ + listening, + transcript, + showFinalWords, + showResults, + browserSupportsSpeechRecognition, + lang, + ]); + + // Cleanup retry timeouts when component unmounts or when activity ends + useEffect(() => { + return () => { + if (retryTimeoutRef.current) { + clearTimeout(retryTimeoutRef.current); + retryTimeoutRef.current = null; + } + if (restartDebounceRef.current) { + clearTimeout(restartDebounceRef.current); + restartDebounceRef.current = null; + } + }; + }, []); + + // Reset retry count when activity ends + useEffect(() => { + if (showResults) { + retryCountRef.current = 0; + isRestartingRef.current = false; + if (retryTimeoutRef.current) { + clearTimeout(retryTimeoutRef.current); + retryTimeoutRef.current = null; + } + if (restartDebounceRef.current) { + clearTimeout(restartDebounceRef.current); + restartDebounceRef.current = null; + } + } + }, [showResults]); + + // Reset component state when it first mounts to ensure clean start + // This ensures TowreFlow starts fresh when rendered after M3 showcase + useEffect(() => { + // Reset all state to initial values when component mounts + setIsStarted(false); + setActiveSet(0); + setCurrentWordSetIndex(0); + setMessage( + "Look at the words.\nYou'll read them soon — left to right, top to bottom" + ); + setShowCountdown(false); + setCount(3); + setShowFinalWords(false); + setCompletedAllSets(false); + setShowResults(false); + setTimer(45); + setWordsAttempted(0); + setWordsPerMinute(0); + setHandPosition({ x: 0, y: 0 }); + setLoading(false); + setCompleted(false); + setRecordedAudioBlob(null); + setTranscripts(""); + setStartTime(null); + setTotalSec(null); + setFinalTranscript(""); + + // Reset refs + transcriptRef.current = ""; + chunksRef.current = []; + shouldBeListeningRef.current = false; + recognitionStartedRef.current = false; + retryCountRef.current = 0; + isRestartingRef.current = false; + + // Stop any existing speech recognition and reset transcript + try { + if (listening && recognitionRef.current) { + recognitionRef.current.stop(); + } + resetTranscript(); + } catch (error) { + // Ignore errors during cleanup + console.log("ℹ️ Error during TowreFlow reset:", error); + } + + console.log( + "🔄 TowreFlow component mounted - state reset to initial values" + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // Empty dependency array - only run on mount (listening and resetTranscript are stable) + + // Check browser support on mount + useEffect(() => { + console.log( + "🔍 Browser supports speech recognition:", + browserSupportsSpeechRecognition + ); + if (!browserSupportsSpeechRecognition) { + console.error("❌ Speech recognition is not supported in this browser!"); + } + }, [browserSupportsSpeechRecognition]); useEffect(() => { let interval; @@ -1129,10 +1598,13 @@ const TowreFlow = ({ const elapsedSeconds = (endTime - startTime) / 1000; //console.log("testingg"); setTotalSec(elapsedSeconds); - stopAudioRecording(); - setLoading(true); clearInterval(interval); setShowResults(true); + // Defer stopAudioRecording to avoid React warning about updating during render + setTimeout(() => { + stopAudioRecording(); + setLoading(true); + }, 0); return 0; } return prevTimer - 1; @@ -1190,21 +1662,21 @@ const TowreFlow = ({ const endTime = Date.now(); const elapsedSeconds = (endTime - startTime) / 1000; setTotalSec(elapsedSeconds); - stopAudioRecording(); - setLoading(true); setCompletedAllSets(true); setShowResults(true); + // Defer stopAudioRecording to avoid React warning about updating during render + setTimeout(() => { + stopAudioRecording(); + setLoading(true); + }, 0); } }; - const startCountdown = () => { + const startCountdown = async () => { + // Start countdown immediately - no delay needed since countdown provides timing setShowCountdown(true); - startAudioRecording(); - SpeechRecognition.startListening({ - continuous: true, - interimResults: true, - language: "en-US", - }); + + // Start countdown let counter = 3; setCount(counter); const interval = setInterval(() => { @@ -1213,7 +1685,7 @@ const TowreFlow = ({ setCount(counter); } else { clearInterval(interval); - setTimeout(() => { + setTimeout(async () => { setShowCountdown(false); setShowFinalWords(true); setTimer(45); @@ -1221,6 +1693,93 @@ const TowreFlow = ({ const wpm = Math.round((wordsAttempted / 45) * 60); setWordsPerMinute(wpm); + + // Start speech recognition after countdown completes + // Check browser support before starting + if (!browserSupportsSpeechRecognition) { + console.error( + "❌ Cannot start speech recognition: Browser does not support it" + ); + return; + } + + // Check microphone permission + try { + const stream = await navigator.mediaDevices.getUserMedia({ + audio: true, + }); + stream.getTracks().forEach((track) => track.stop()); // Stop immediately, we just needed permission + console.log("✅ Microphone permission granted"); + } catch (error) { + console.error("❌ Microphone permission denied or error:", error); + alert( + "Microphone access is required for speech recognition. Please allow microphone access and try again." + ); + return; + } + + // Start audio recording + await startAudioRecording(); + + // Reset transcript before starting + resetTranscript(); + transcriptRef.current = ""; + + console.log( + "🎤 Starting speech recognition, language:", + getBrowserLanguage(lang) + ); + + try { + shouldBeListeningRef.current = true; + retryCountRef.current = 0; // Reset retry count when starting fresh + isRestartingRef.current = false; // Reset restart flag + recognitionStartedRef.current = false; // Reset start flag + + // Always stop first if listening to ensure clean state + // This prevents conflicts from previous sessions + if (listening && recognitionRef.current) { + try { + console.log( + "🛑 Stopping existing recognition for clean restart" + ); + recognitionRef.current.stop(); + // Wait for clean stop before starting again + await new Promise((resolve) => setTimeout(resolve, 500)); + } catch (stopError) { + console.log( + "ℹ️ Error stopping recognition (may already be stopped):", + stopError + ); + } + } + + // Reset transcript before starting fresh + resetTranscript(); + transcriptRef.current = ""; + + // Start recognition with a small delay to ensure clean state + await new Promise((resolve) => setTimeout(resolve, 200)); + + if (recognitionRef.current) { + recognitionRef.current.lang = getBrowserLanguage(lang); + recognitionRef.current.start(); + console.log("✅ Speech recognition start command sent"); + } else { + console.error("❌ Recognition instance not initialized"); + } + + // Don't access the recognition instance - let the library handle everything + // Accessing it can interfere with the library's internal handlers and prevent transcript updates + // We'll rely on the listening state and transcript from the hook instead + + // Note: We don't check listening state here because it uses stale closure values + // The listening state will be tracked by the useEffect that watches listening changes + recognitionStartedRef.current = true; + } catch (error) { + console.error("❌ Error starting speech recognition:", error); + shouldBeListeningRef.current = false; + } }, 500); } }, 1000); @@ -1275,9 +1834,7 @@ const TowreFlow = ({ }; mediaRecorder.onstop = async () => { - //console.log("Recording stopped."); if (chunksRef.current.length === 0) { - console.warn("No data to create blob."); return; } @@ -1287,92 +1844,153 @@ const TowreFlow = ({ streamRef.current?.getTracks().forEach((track) => track.stop()); - try { - setLoading(true); - const transcriber = await pipeline( - "automatic-speech-recognition", - "Xenova/whisper-tiny", - { quantized: true } + // Validate audio blob before processing + if (!audioBlob || audioBlob.size === 0) { + setTranscripts(transcriptRef.current || ""); + const transcriptWords = normalize(transcriptRef.current || "").filter( + (w) => w && w.trim().length > 0 + ); + const transcriptPhonetics = new Set( + transcriptWords.map(getPhonetic).filter((ph) => ph && ph.length > 0) ); - const audioUrl = URL.createObjectURL(audioBlob); - - const output = await transcriber(audioUrl, { - chunk_length_s: 20, - stride_length_s: 5, - }); + allWords.forEach((word) => { + const lower = word?.title?.toLowerCase()?.trim(); - //console.log("Transcription result:", output.text); + if (!lower || lower.length === 0) { + word.isCorrect = false; + return; + } - const transcripts = output.text; - setTranscripts(transcripts); - const transcriptWords = normalize(transcripts); - const transcriptPhonetics = new Set(transcriptWords.map(getPhonetic)); + const wordPhonetic = getPhonetic(lower); + const hasValidPhonetic = wordPhonetic && wordPhonetic.length > 0; - allWords.forEach((word) => { - const lower = word?.title?.toLowerCase(); const isSpoken = transcriptWords.includes(lower) || - transcriptPhonetics.has(getPhonetic(lower)); + (hasValidPhonetic && transcriptPhonetics.has(wordPhonetic)); word.isCorrect = isSpoken; }); - const base64Audio = await blobToBase64(audioBlob); - - const sessionId = getLocalData("sessionId"); - var audioFileName = ""; - let getContentId = "towre"; - audioFileName = `${ - process.env.REACT_APP_CHANNEL - }/${sessionId}-${Date.now()}-${getContentId}.wav`; - - const command = new PutObjectCommand({ - Bucket: process.env.REACT_APP_AWS_S3_BUCKET_NAME, - Key: audioFileName, - Body: Uint8Array.from(window.atob(base64Audio), (c) => - c.charCodeAt(0) - ), - ContentType: "audio/wav", - }); - try { - await S3Client.send(command); - //console.log("Upload success"); - } catch (err) { - console.error("Upload failed:", err); - } - - await addTowreRecord(audioFileName, allWords); - setLoading(false); setCompleted(true); - } catch (error) { - console.error("Error during transcription:", error); - //console.log("transcriptok", transcriptRef.current); - setTranscripts(transcriptRef.current); - const transcriptWords = normalize(transcriptRef.current); - const transcriptPhonetics = new Set(transcriptWords.map(getPhonetic)); + return; + } + + // Check minimum audio size (at least 1KB to ensure valid audio) + const MIN_AUDIO_SIZE = 1024; // 1KB minimum + if (audioBlob.size < MIN_AUDIO_SIZE) { + console.warn( + `Audio blob too small (${audioBlob.size} bytes), using fallback transcription` + ); + setTranscripts(transcriptRef.current || ""); + const transcriptWords = normalize(transcriptRef.current || "").filter( + (w) => w && w.trim().length > 0 + ); + const transcriptPhonetics = new Set( + transcriptWords.map(getPhonetic).filter((ph) => ph && ph.length > 0) + ); allWords.forEach((word) => { - const lower = word?.title?.toLowerCase(); + const lower = word?.title?.toLowerCase()?.trim(); + + if (!lower || lower.length === 0) { + word.isCorrect = false; + return; + } + + const wordPhonetic = getPhonetic(lower); + const hasValidPhonetic = wordPhonetic && wordPhonetic.length > 0; + const isSpoken = transcriptWords.includes(lower) || - transcriptPhonetics.has(getPhonetic(lower)); + (hasValidPhonetic && transcriptPhonetics.has(wordPhonetic)); word.isCorrect = isSpoken; }); - try { - await addTowreRecord(audioFileName, allWords); - } catch (apiErr) { - console.error("Error sending TOWRE record:", apiErr); - setLoading(false); - setCompleted(true); - } - setLoading(false); setCompleted(true); + return; + } + + // Use browser speech recognition as primary method (it's already working!) + // Browser SR captures transcript in real-time during recording via transcriptRef.current + setLoading(true); + + // Get transcript from browser speech recognition (already captured during recording) + const transcripts = transcriptRef.current || ""; + setTranscripts(transcripts); + const transcriptWords = normalize(transcripts).filter( + (w) => w && w.trim().length > 0 + ); // Filter out empty strings + const transcriptPhonetics = new Set( + transcriptWords.map(getPhonetic).filter((ph) => ph && ph.length > 0) // Filter out empty phonetic codes + ); + + allWords.forEach((word) => { + const lower = word?.title?.toLowerCase()?.trim(); + + // Skip empty or invalid words + if (!lower || lower.length === 0) { + word.isCorrect = false; + return; + } + + // Get phonetic code for the word (only if valid) + const wordPhonetic = getPhonetic(lower); + const hasValidPhonetic = wordPhonetic && wordPhonetic.length > 0; + + // Match only if: + // 1. Exact match in transcript words, OR + // 2. Phonetic match (only if both have valid phonetic codes) + const isSpoken = + transcriptWords.includes(lower) || + (hasValidPhonetic && transcriptPhonetics.has(wordPhonetic)); + + word.isCorrect = isSpoken; + }); + + // Try to upload audio and save record (non-blocking - continue even if it fails) + if (audioBlob && audioBlob.size > 0) { + try { + const base64Audio = await blobToBase64(audioBlob); + + const sessionId = getLocalData("sessionId"); + var audioFileName = ""; + let getContentId = "towre"; + audioFileName = `${ + process.env.REACT_APP_CHANNEL + }/${sessionId}-${Date.now()}-${getContentId}.wav`; + + const command = new PutObjectCommand({ + Bucket: process.env.REACT_APP_AWS_S3_BUCKET_NAME, + Key: audioFileName, + Body: Uint8Array.from(window.atob(base64Audio), (c) => + c.charCodeAt(0) + ), + ContentType: "audio/wav", + }); + + try { + await S3Client.send(command); + } catch (uploadErr) { + // S3 upload failed (non-critical) - continue + } + + try { + await addTowreRecord(audioFileName, allWords, lang); + } catch (apiErr) { + // Error saving TOWRE record (non-critical) - continue + } + } catch (processErr) { + // Error processing audio for upload (non-critical) - continue + // Continue even if audio processing fails + } } + + setLoading(false); + setCompleted(true); }; mediaRecorder.start(); @@ -1382,15 +2000,35 @@ const TowreFlow = ({ }; const stopAudioRecording = () => { - SpeechRecognition.stopListening(); + shouldBeListeningRef.current = false; + retryCountRef.current = 0; // Reset retry count when stopping + isRestartingRef.current = false; // Reset restart flag + + // Clear any pending retry timeouts + if (retryTimeoutRef.current) { + clearTimeout(retryTimeoutRef.current); + retryTimeoutRef.current = null; + } + + // Clear debounce timeout + if (restartDebounceRef.current) { + clearTimeout(restartDebounceRef.current); + restartDebounceRef.current = null; + } + + console.log( + "🛑 Stopping speech recognition, final transcript:", + transcriptRef.current + ); + if (recognitionRef.current) { + recognitionRef.current.stop(); + } setFinalTranscript(transcriptRef.current); if ( mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive" ) { mediaRecorderRef.current.stop(); - } else { - console.warn("Recorder already inactive or null."); } }; @@ -1427,7 +2065,7 @@ const TowreFlow = ({ currentStep, level, progressData, - showProgress, + showProgress: false, // Hide progress bar for Towre Flow playTeacherAudio, handleBack, disableScreen, @@ -1683,7 +2321,15 @@ const TowreFlow = ({ style={{ position: "absolute", fontWeight: 700, - fontSize: isMobile ? 12 : 20, + fontSize: + lang === "te" + ? isMobile + ? 14 + : 24 + : isMobile + ? 12 + : 20, + fontFamily: getFontFamily(lang), }} > {wordObj.title} @@ -1848,7 +2494,15 @@ const TowreFlow = ({ position: "absolute", color: isActive ? "#000" : "#aaa", fontWeight: 600, - fontSize: isMobile ? 11 : 20, + fontSize: + lang === "te" + ? isMobile + ? 13 + : 24 + : isMobile + ? 11 + : 20, + fontFamily: getFontFamily(lang), }} > {wordObj.title} @@ -2119,4 +2773,62 @@ const TowreFlow = ({ ); }; -export default TowreFlow; +// Memoize TowreFlow to prevent unnecessary re-renders +// Only re-render when props that affect speech recognition or component behavior change +// Note: Internal state changes (like transcript from useSpeechRecognition) will still trigger re-renders +const TowreFlowMemo = memo(TowreFlow, (prevProps, nextProps) => { + // Props that should trigger re-render + const criticalProps = [ + "level", + "currentStep", + "steps", + "contentId", + "contentType", + "enableNext", + "showTimer", + "isShowCase", + "loading", + "disableScreen", + "currentImg", + "parentWords", + "background", + ]; + + // Check if any critical props changed + for (const prop of criticalProps) { + if (prevProps[prop] !== nextProps[prop]) { + return false; // Re-render + } + } + + // Check progressData.currentPracticeStep (the only part we actually use) + const prevStep = prevProps.progressData?.currentPracticeStep; + const nextStep = nextProps.progressData?.currentPracticeStep; + if (prevStep !== nextStep) { + return false; // Re-render + } + + // Check function references that might affect behavior + const functionProps = [ + "handleNext", + "handleBack", + "setOpenMessageDialog", + "playTeacherAudio", + ]; + + for (const prop of functionProps) { + if (prevProps[prop] !== nextProps[prop]) { + return false; // Re-render + } + } + + // Ignore changes to these props (they change frequently but don't affect speech recognition) + // vocabCount, wordCount, showProgress (we override it anyway) + // These can change without causing a re-render + // NOTE: Internal state from useSpeechRecognition (transcript, listening) will still + // trigger re-renders because they're managed inside the component, not via props + + return true; // Don't re-render +}); + +export default TowreFlowMemo; diff --git a/src/components/Practice/WordWall.jsx b/src/components/Practice/WordWall.jsx index 631c9df9..bb025cce 100644 --- a/src/components/Practice/WordWall.jsx +++ b/src/components/Practice/WordWall.jsx @@ -28,6 +28,7 @@ import Giftbox from "../../assets/Giftbox.json"; import { getCorrectPracticeWords } from "../../services/orchestration/orchestrationService"; import { shuffle } from "lodash"; import { getLocalData, setLocalData } from "../../utils/constants"; +import { getFontFamily } from "../../utils/fontUtils"; import giftscoreImg from "../../assets/giftscore.svg"; import redboxImg from "../../assets/redbox.svg"; import greenboxImg from "../../assets/greenbox.svg"; @@ -203,15 +204,53 @@ const WordWall = ({ try { const response = await getCorrectPracticeWords("false"); const correctWords = response?.data || []; + const selectedLang = getLocalData("lang") || "en"; - const formattedItems = correctWords?.map((item) => ({ - image_url: item?.imagePath || item.mechanics_data?.[0].image_url, - text: item?.contentSourceData?.[0]?.text, - audio_en: `${item?.contentId}.wav`, - audio_hi: item?.multilingual?.kn?.audio_url, - })); + // First, filter items by top-level language field to only include items matching selected language + const filteredWords = correctWords.filter((item) => { + // Check if item's top-level language matches + if (item?.language === selectedLang) { + return true; + } + // Also check if contentSourceData has an entry for the selected language + return item?.contentSourceData?.some( + (data) => data?.language === selectedLang + ); + }); + + const formattedItems = filteredWords?.map((item) => { + // Filter contentSourceData to find the entry matching the selected language + const contentData = + item?.contentSourceData?.find( + (data) => data?.language === selectedLang + ) || item?.contentSourceData?.[0]; // Fallback to first entry if language not found + + // Get multilingual language code for audio (maps nativeLang to multilingual object keys) + const getMultilingualLangCode = () => { + const nativeLang = getLocalData("nativeLang"); + const langCodeMap = { + ka: "kn", // Kannada (from LanguageModal -> multilingual key) + kn: "kn", // Kannada (from AllLanguages) + tn: "ta", // Tamil (from LanguageModal -> multilingual key) + ta: "ta", // Tamil (from AllLanguages) + te: "te", // Telugu + hi: "hi", // Hindi + gu: "gu", // Gujarati + or: "or", // Odia + }; + return langCodeMap[nativeLang] || "kn"; // Default to Kannada if not found + }; + const multilingualLangCode = getMultilingualLangCode(); + + return { + image_url: item?.imagePath || item.mechanics_data?.[0].image_url, + text: contentData?.text, + audio_en: `${item?.contentId}.wav`, + audio_hi: item?.multilingual?.[multilingualLangCode]?.audio_url, + }; + }); - if (correctWords?.length < 3) { + if (formattedItems?.length < 3) { setLocalData("wordWall", false); navigate("/practice"); } @@ -1235,10 +1274,10 @@ const WordWall = ({ style={{ color: "#333F61", fontWeight: 600, - fontSize: "50px", + fontSize: lang === "te" ? "56px" : "50px", lineHeight: "60px", letterSpacing: "1%", - fontFamily: "Quicksand", + fontFamily: getFontFamily(lang), }} > {currentAnswer?.text} diff --git a/src/components/Practice/ZoomableImage.jsx b/src/components/Practice/ZoomableImage.jsx new file mode 100644 index 00000000..69831964 --- /dev/null +++ b/src/components/Practice/ZoomableImage.jsx @@ -0,0 +1,200 @@ +import React, { useState } from "react"; +import { Box, Modal, useMediaQuery, useTheme } from "@mui/material"; +import ZoomInIcon from "@mui/icons-material/ZoomIn"; +import CloseIcon from "@mui/icons-material/Close"; +import * as Assets from "../../utils/imageAudioLinks"; + +/** + * ZoomableImage - A reusable component for displaying images with zoom functionality + * + * @param {string} src - Image source URL + * @param {string} alt - Alt text for the image + * @param {object} imageStyle - Custom styles for the image + * @param {string} iconPosition - Position of zoom icon: "top-left" | "bottom-right" (default: "top-left") + * @param {boolean} useCustomIcon - Whether to use custom icon image instead of Material-UI icon (default: false) + * @param {object} containerStyle - Custom styles for the image container + * @param {boolean} showGradientOverlay - Whether to show gradient overlay (default: true) + */ +const ZoomableImage = ({ + src, + alt = "contentImage", + imageStyle = {}, + iconPosition = "top-left", + useCustomIcon = false, + containerStyle = {}, + showGradientOverlay = true, +}) => { + const [zoomOpen, setZoomOpen] = useState(false); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); + + // Default image styles + const defaultImageStyle = { + borderRadius: "20px", + maxWidth: "100%", + height: isMobile ? "150px" : "250px", + cursor: "pointer", + ...imageStyle, + }; + + // Default container styles + const defaultContainerStyle = { + position: "relative", + cursor: "zoom-in", + width: "fit-content", + ...containerStyle, + }; + + if (!src) { + return null; + } + + return ( + <> + + {alt} setZoomOpen(true)} + /> + + {showGradientOverlay && ( + <> + {useCustomIcon && iconPosition === "bottom-right" ? ( + + setZoomOpen(true)} + height="65px" + width="65px" + alt="Zoom" + style={{ cursor: "pointer" }} + /> + + ) : ( + + setZoomOpen(true)} + sx={{ + color: "white", + fontSize: "22px", + cursor: "pointer", + }} + /> + + )} + + )} + + + {/* Modal for zoomed image */} + setZoomOpen(false)} + sx={{ + display: "flex", + alignItems: "center", + justifyContent: "center", + zIndex: 99999, + }} + > + + {/* Gradient overlay at the top of the zoomed image */} + + {/* Close icon */} + {useCustomIcon ? ( + setZoomOpen(false)} + style={{ + marginTop: "20px", + cursor: "pointer", + width: "24px", + height: "24px", + }} + alt="Close" + /> + ) : ( + setZoomOpen(false)} + sx={{ + color: "white", + fontSize: isMobile ? "20px" : "24px", + cursor: "pointer", + backgroundColor: "rgba(0, 0, 0, 0.5)", + borderRadius: "50%", + padding: "4px", + }} + /> + )} + + + {/* Zoomed image */} + {alt + + + + ); +}; + +export default ZoomableImage; diff --git a/src/constants/audioDiagnosticPrompts.js b/src/constants/audioDiagnosticPrompts.js new file mode 100644 index 00000000..8d70f887 --- /dev/null +++ b/src/constants/audioDiagnosticPrompts.js @@ -0,0 +1,40 @@ +/** + * Multi-lingual audio prompts for audio diagnostic test + * These prompts are used for children to listen and repeat + */ + +export const AUDIO_DIAGNOSTIC_PROMPTS = { + en: ["This is a cat", "It is a table"], + hi: ["यह एक बिल्ली है", "यह एक मेज है"], + ta: ["இது ஒரு பூனை", "இது ஒரு மேசை"], + tn: ["இது ஒரு பூனை", "இது ஒரு மேசை"], // Tamil alternative code + te: ["ఇది ఒక పిల్లి", "ఇది ఒక మేజ్"], + ka: ["ಇದು ಒಂದು ಬೆಕ್ಕು", "ಇದು ಒಂದು ಮೇಜು"], + kn: ["ಇದು ಒಂದು ಬೆಕ್ಕು", "ಇದು ಒಂದು ಮೇಜು"], // Kannada alternative code +}; + +/** + * Get audio prompts for a specific language + * @param {string} lang - Language code (en, hi, ta, tn, te, ka, kn, etc.) + * @returns {string[]} Array of prompts for the language, defaults to English + */ +export const getAudioPromptsByLang = (lang) => { + // Handle language code aliases + const langMap = { + tn: "ta", // Map tn to ta (Tamil) + kn: "ka", // Map kn to ka (Kannada) + }; + + const mappedLang = langMap[lang] || lang; + return AUDIO_DIAGNOSTIC_PROMPTS[mappedLang] || AUDIO_DIAGNOSTIC_PROMPTS.en; +}; + +/** + * Get a random prompt for a specific language + * @param {string} lang - Language code (en, hi, ta, te, ka, etc.) + * @returns {string} A random prompt from the language-specific prompts + */ +export const getRandomAudioPrompt = (lang) => { + const prompts = getAudioPromptsByLang(lang); + return prompts[Math.floor(Math.random() * prompts.length)]; +}; diff --git a/src/constants/audioDiagnosticTranslations.js b/src/constants/audioDiagnosticTranslations.js new file mode 100644 index 00000000..8802a3b1 --- /dev/null +++ b/src/constants/audioDiagnosticTranslations.js @@ -0,0 +1,278 @@ +/** + * Multi-lingual translations for audio diagnostic UI + */ + +export const AUDIO_DIAGNOSTIC_TRANSLATIONS = { + en: { + // Speech bubble messages + listenAndRepeat: "Hi! Listen to the audio and repeat it!", + nowRepeat: "Now repeat what you heard!", + keepSpeaking: "Great! Keep speaking...", + micTestPassed: "Awesome! You did great!", + testMicrophone: "Let's test your microphone!", + listenToVoice: "Now let's listen to your voice!", + canYouHear: "Can you hear it?", + speakerTestPassed: "Perfect! You're all set!", + testSpeakers: "Let's test your speakers!", + + // Button labels + clickToListen: "Click to listen", + clickToListenAgain: "Click to listen again", + playingAudio: "Playing audio...", + repeatNow: "REPEAT NOW", + tryAgain: "TRY AGAIN", + continue: "CONTINUE", + skip: "Skip", + + // Error messages + micErrorNoAudio: "We can't hear you! Check if your microphone is on.", + micErrorMuted: "We can't hear you! Make sure your microphone is on.", + micErrorPermission: + "We need permission to hear you! Please allow microphone access.", + micErrorGeneric: "Something went wrong. Please try again!", + speakerError: "We couldn't play the sound! Check if your speakers are on!", + speakerErrorGeneric: "Oops! The sound didn't play. Try again!", + speakerErrorNoTest: + "We couldn't test your speakers. Make sure they're turned on!", + + // Success messages + allTestsPassed: "All tests passed! You're ready to start learning!", + }, + hi: { + // Speech bubble messages + listenAndRepeat: "नमस्ते! ऑडियो सुनें और दोहराएं!", + nowRepeat: "अब जो आपने सुना, उसे दोहराएं!", + keepSpeaking: "बहुत बढ़िया! बोलते रहें...", + micTestPassed: "शानदार! आपने बहुत अच्छा किया!", + testMicrophone: "आइए अपने माइक्रोफोन का परीक्षण करें!", + listenToVoice: "अब आइए अपनी आवाज़ सुनें!", + canYouHear: "क्या आप इसे सुन सकते हैं?", + speakerTestPassed: "बिल्कुल सही! आप तैयार हैं!", + testSpeakers: "आइए अपने स्पीकर का परीक्षण करें!", + + // Button labels + clickToListen: "सुनने के लिए क्लिक करें", + clickToListenAgain: "फिर से सुनने के लिए क्लिक करें", + playingAudio: "ऑडियो चल रहा है...", + repeatNow: "अभी दोहराएं", + tryAgain: "फिर से कोशिश करें", + continue: "जारी रखें", + skip: "छोड़ें", + + // Error messages + micErrorNoAudio: + "हम आपकी आवाज़ नहीं सुन सकते! जांचें कि क्या आपका माइक्रोफोन चालू है।", + micErrorMuted: + "हम आपकी आवाज़ नहीं सुन सकते! सुनिश्चित करें कि आपका माइक्रोफोन चालू है।", + micErrorPermission: + "हमें आपकी आवाज़ सुनने की अनुमति चाहिए! कृपया माइक्रोफोन पहुंच की अनुमति दें।", + micErrorGeneric: "कुछ गलत हो गया। कृपया फिर से कोशिश करें!", + speakerError: "हम ध्वनि नहीं बजा सके! जांचें कि क्या आपके स्पीकर चालू हैं!", + speakerErrorGeneric: "ओह! ध्वनि नहीं बजी। फिर से कोशिश करें!", + speakerErrorNoTest: + "हम आपके स्पीकर का परीक्षण नहीं कर सके। सुनिश्चित करें कि वे चालू हैं!", + + // Success messages + allTestsPassed: "सभी परीक्षण पास! आप सीखना शुरू करने के लिए तैयार हैं!", + }, + ta: { + // Speech bubble messages + listenAndRepeat: "வணக்கம்! ஆடியோவைக் கேட்டு மீண்டும் சொல்லுங்கள்!", + nowRepeat: "இப்போது நீங்கள் கேட்டதை மீண்டும் சொல்லுங்கள்!", + keepSpeaking: "நன்றாக! தொடர்ந்து பேசுங்கள்...", + micTestPassed: "அருமை! நீங்கள் நன்றாக செய்தீர்கள்!", + testMicrophone: "உங்கள் மைக்ரோஃபோனை சோதிக்கலாம்!", + listenToVoice: "இப்போது உங்கள் குரலைக் கேட்போம்!", + canYouHear: "நீங்கள் கேட்க முடிகிறதா?", + speakerTestPassed: "சரியாக! நீங்கள் தயார்!", + testSpeakers: "உங்கள் ஸ்பீக்கர்களை சோதிக்கலாம்!", + + // Button labels + clickToListen: "கேட்க கிளிக் செய்யவும்", + clickToListenAgain: "மீண்டும் கேட்க கிளிக் செய்யவும்", + playingAudio: "ஆடியோ இயங்குகிறது...", + repeatNow: "இப்போது மீண்டும் சொல்லுங்கள்", + tryAgain: "மீண்டும் முயற்சிக்கவும்", + continue: "தொடரவும்", + skip: "தவிர்க்கவும்", + + // Error messages + micErrorNoAudio: + "உங்கள் குரலை நாங்கள் கேட்க முடியவில்லை! உங்கள் மைக்ரோஃபோன் இயக்கத்தில் உள்ளதா என்பதைச் சரிபார்க்கவும்।", + micErrorMuted: + "உங்கள் குரலை நாங்கள் கேட்க முடியவில்லை! உங்கள் மைக்ரோஃபோன் இயக்கத்தில் உள்ளது என்பதை உறுதிப்படுத்தவும்।", + micErrorPermission: + "உங்களைக் கேட்க எங்களுக்கு அனுமதி தேவை! தயவுசெய்து மைக்ரோஃபோன் அணுகலை அனுமதிக்கவும்।", + micErrorGeneric: "ஏதோ தவறு நடந்தது. தயவுசெய்து மீண்டும் முயற்சிக்கவும்!", + speakerError: + "ஒலியை இயக்க முடியவில்லை! உங்கள் ஸ்பீக்கர்கள் இயக்கத்தில் உள்ளதா என்பதைச் சரிபார்க்கவும்!", + speakerErrorGeneric: "ஓ! ஒலி இயங்கவில்லை. மீண்டும் முயற்சிக்கவும்!", + speakerErrorNoTest: + "உங்கள் ஸ்பீக்கர்களை நாங்கள் சோதிக்க முடியவில்லை. அவை இயக்கத்தில் உள்ளதா என்பதை உறுதிப்படுத்தவும்!", + + // Success messages + allTestsPassed: + "அனைத்து சோதனைகளும் தேர்ச்சி! நீங்கள் கற்றுக்கொள்ளத் தயார்!", + }, + te: { + // Speech bubble messages + listenAndRepeat: "హాయ్! ఆడియో విని మళ్లీ చెప్పండి!", + nowRepeat: "ఇప్పుడు మీరు విన్నదాన్ని మళ్లీ చెప్పండి!", + keepSpeaking: "బాగుంది! మాట్లాడుతూ ఉండండి...", + micTestPassed: "అద్భుతం! మీరు చాలా బాగా చేసారు!", + testMicrophone: "మీ మైక్రోఫోన్ను పరీక్షిద్దాం!", + listenToVoice: "ఇప్పుడు మీ స్వరాన్ని వినండి!", + canYouHear: "మీరు వినగలరా?", + speakerTestPassed: "పర్ఫెక్ట్! మీరు సిద్ధంగా ఉన్నారు!", + testSpeakers: "మీ స్పీకర్లను పరీక్షిద్దాం!", + + // Button labels + clickToListen: "వినడానికి క్లిక్ చేయండి", + clickToListenAgain: "మళ్లీ వినడానికి క్లిక్ చేయండి", + playingAudio: "ఆడియో ప్లే అవుతోంది...", + repeatNow: "ఇప్పుడు మళ్లీ చెప్పండి", + tryAgain: "మళ్లీ ప్రయత్నించండి", + continue: "కొనసాగించండి", + skip: "దాటవేయి", + + // Error messages + micErrorNoAudio: + "మేము మీ స్వరాన్ని వినలేము! మీ మైక్రోఫోన్ ఆన్ ఉందో లేదో తనిఖీ చేయండి.", + micErrorMuted: + "మేము మీ స్వరాన్ని వినలేము! మీ మైక్రోఫోన్ ఆన్ ఉందని నిర్ధారించుకోండి.", + micErrorPermission: + "మీరిని వినడానికి మాకు అనుమతి కావాలి! దయచేసి మైక్రోఫోన్ ప్రాప్యతను అనుమతించండి.", + micErrorGeneric: "ఏదో తప్పు జరిగింది. దయచేసి మళ్లీ ప్రయత్నించండి!", + speakerError: + "మేము ధ్వనిని ప్లే చేయలేకపోయాము! మీ స్పీకర్లు ఆన్ ఉన్నాయో లేదో తనిఖీ చేయండి!", + speakerErrorGeneric: "ఓహ్! ధ్వని ప్లే కాలేదు. మళ్లీ ప్రయత్నించండి!", + speakerErrorNoTest: + "మేము మీ స్పీకర్లను పరీక్షించలేకపోయాము. అవి ఆన్ అయ్యాయో లేదో నిర్ధారించుకోండి!", + + // Success messages + allTestsPassed: + "అన్ని పరీక్షలు పాస్! మీరు నేర్చుకోవడానికి సిద్ధంగా ఉన్నారు!", + }, + ka: { + // Speech bubble messages + listenAndRepeat: "ನಮಸ್ಕಾರ! ಆಡಿಯೋವನ್ನು ಕೇಳಿ ಮತ್ತು ಪುನರಾವರ್ತಿಸಿ!", + nowRepeat: "ಈಗ ನೀವು ಕೇಳಿದ್ದನ್ನು ಪುನರಾವರ್ತಿಸಿ!", + keepSpeaking: "ಉತ್ತಮ! ಮಾತನಾಡುತ್ತಲೇ ಇರಿ...", + micTestPassed: "ಅದ್ಭುತ! ನೀವು ಚೆನ್ನಾಗಿ ಮಾಡಿದ್ದೀರಿ!", + testMicrophone: "ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಪರೀಕ್ಷಿಸೋಣ!", + listenToVoice: "ಈಗ ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ಕೇಳೋಣ!", + canYouHear: "ನೀವು ಕೇಳಬಲ್ಲಿರಾ?", + speakerTestPassed: "ಪರಿಪೂರ್ಣ! ನೀವು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ!", + testSpeakers: "ನಿಮ್ಮ ಸ್ಪೀಕರ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸೋಣ!", + + // Button labels + clickToListen: "ಕೇಳಲು ಕ್ಲಿಕ್ ಮಾಡಿ", + clickToListenAgain: "ಮತ್ತೆ ಕೇಳಲು ಕ್ಲಿಕ್ ಮಾಡಿ", + playingAudio: "ಆಡಿಯೋ ಪ್ಲೇ ಆಗುತ್ತಿದೆ...", + repeatNow: "ಈಗ ಪುನರಾವರ್ತಿಸಿ", + tryAgain: "ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ", + continue: "ಮುಂದುವರಿಸಿ", + skip: "ಬಿಟ್ಟುಬಿಡಿ", + + // Error messages + micErrorNoAudio: + "ನಾವು ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಸಾಧ್ಯವಿಲ್ಲ! ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ.", + micErrorMuted: + "ನಾವು ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಸಾಧ್ಯವಿಲ್ಲ! ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಆನ್ ಆಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ.", + micErrorPermission: + "ನಿಮ್ಮನ್ನು ಕೇಳಲು ನಮಗೆ ಅನುಮತಿ ಬೇಕು! ದಯವಿಟ್ಟು ಮೈಕ್ರೋಫೋನ್ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ.", + micErrorGeneric: "ಏನೋ ತಪ್ಪಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!", + speakerError: + "ನಾವು ಧ್ವನಿಯನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ! ನಿಮ್ಮ ಸ್ಪೀಕರ್ಗಳು ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ!", + speakerErrorGeneric: "ಓಹ್! ಧ್ವನಿ ಪ್ಲೇ ಆಗಲಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!", + speakerErrorNoTest: + "ನಾವು ನಿಮ್ಮ ಸ್ಪೀಕರ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅವು ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ!", + + // Success messages + allTestsPassed: "ಎಲ್ಲಾ ಪರೀಕ್ಷೆಗಳು ಪಾಸ್! ನೀವು ಕಲಿಯಲು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ!", + }, + kn: { + // Same as Kannada (ka) + listenAndRepeat: "ನಮಸ್ಕಾರ! ಆಡಿಯೋವನ್ನು ಕೇಳಿ ಮತ್ತು ಪುನರಾವರ್ತಿಸಿ!", + nowRepeat: "ಈಗ ನೀವು ಕೇಳಿದ್ದನ್ನು ಪುನರಾವರ್ತಿಸಿ!", + keepSpeaking: "ಉತ್ತಮ! ಮಾತನಾಡುತ್ತಲೇ ಇರಿ...", + micTestPassed: "ಅದ್ಭುತ! ನೀವು ಚೆನ್ನಾಗಿ ಮಾಡಿದ್ದೀರಿ!", + testMicrophone: "ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಪರೀಕ್ಷಿಸೋಣ!", + listenToVoice: "ಈಗ ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ಕೇಳೋಣ!", + canYouHear: "ನೀವು ಕೇಳಬಲ್ಲಿರಾ?", + speakerTestPassed: "ಪರಿಪೂರ್ಣ! ನೀವು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ!", + testSpeakers: "ನಿಮ್ಮ ಸ್ಪೀಕರ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸೋಣ!", + clickToListen: "ಕೇಳಲು ಕ್ಲಿಕ್ ಮಾಡಿ", + clickToListenAgain: "ಮತ್ತೆ ಕೇಳಲು ಕ್ಲಿಕ್ ಮಾಡಿ", + playingAudio: "ಆಡಿಯೋ ಪ್ಲೇ ಆಗುತ್ತಿದೆ...", + repeatNow: "ಈಗ ಪುನರಾವರ್ತಿಸಿ", + tryAgain: "ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ", + continue: "ಮುಂದುವರಿಸಿ", + skip: "ಬಿಟ್ಟುಬಿಡಿ", + micErrorNoAudio: + "ನಾವು ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಸಾಧ್ಯವಿಲ್ಲ! ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ.", + micErrorMuted: + "ನಾವು ನಿಮ್ಮ ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಸಾಧ್ಯವಿಲ್ಲ! ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಆನ್ ಆಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ.", + micErrorPermission: + "ನಿಮ್ಮನ್ನು ಕೇಳಲು ನಮಗೆ ಅನುಮತಿ ಬೇಕು! ದಯವಿಟ್ಟು ಮೈಕ್ರೋಫೋನ್ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ.", + micErrorGeneric: "ಏನೋ ತಪ್ಪಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!", + speakerError: + "ನಾವು ಧ್ವನಿಯನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ! ನಿಮ್ಮ ಸ್ಪೀಕರ್ಗಳು ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ!", + speakerErrorGeneric: "ಓಹ್! ಧ್ವನಿ ಪ್ಲೇ ಆಗಲಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!", + speakerErrorNoTest: + "ನಾವು ನಿಮ್ಮ ಸ್ಪೀಕರ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅವು ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ!", + allTestsPassed: "ಎಲ್ಲಾ ಪರೀಕ್ಷೆಗಳು ಪಾಸ್! ನೀವು ಕಲಿಯಲು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ!", + }, + tn: { + // Same as Tamil (ta) - alternative code + listenAndRepeat: "வணக்கம்! ஆடியோவைக் கேட்டு மீண்டும் சொல்லுங்கள்!", + nowRepeat: "இப்போது நீங்கள் கேட்டதை மீண்டும் சொல்லுங்கள்!", + keepSpeaking: "நன்றாக! தொடர்ந்து பேசுங்கள்...", + micTestPassed: "அருமை! நீங்கள் நன்றாக செய்தீர்கள்!", + testMicrophone: "உங்கள் மைக்ரோஃபோனை சோதிக்கலாம்!", + listenToVoice: "இப்போது உங்கள் குரலைக் கேட்போம்!", + canYouHear: "நீங்கள் கேட்க முடிகிறதா?", + speakerTestPassed: "சரியாக! நீங்கள் தயார்!", + testSpeakers: "உங்கள் ஸ்பீக்கர்களை சோதிக்கலாம்!", + clickToListen: "கேட்க கிளிக் செய்யவும்", + clickToListenAgain: "மீண்டும் கேட்க கிளிக் செய்யவும்", + playingAudio: "ஆடியோ இயங்குகிறது...", + repeatNow: "இப்போது மீண்டும் சொல்லுங்கள்", + tryAgain: "மீண்டும் முயற்சிக்கவும்", + continue: "தொடரவும்", + skip: "தவிர்க்கவும்", + micErrorNoAudio: + "உங்கள் குரலை நாங்கள் கேட்க முடியவில்லை! உங்கள் மைக்ரோஃபோன் இயக்கத்தில் உள்ளதா என்பதைச் சரிபார்க்கவும்।", + micErrorMuted: + "உங்கள் குரலை நாங்கள் கேட்க முடியவில்லை! உங்கள் மைக்ரோஃபோன் இயக்கத்தில் உள்ளது என்பதை உறுதிப்படுத்தவும்।", + micErrorPermission: + "உங்களைக் கேட்க எங்களுக்கு அனுமதி தேவை! தயவுசெய்து மைக்ரோஃபோன் அணுகலை அனுமதிக்கவும்।", + micErrorGeneric: "ஏதோ தவறு நடந்தது. தயவுசெய்து மீண்டும் முயற்சிக்கவும்!", + speakerError: + "ஒலியை இயக்க முடியவில்லை! உங்கள் ஸ்பீக்கர்கள் இயக்கத்தில் உள்ளதா என்பதைச் சரிபார்க்கவும்!", + speakerErrorGeneric: "ஓ! ஒலி இயங்கவில்லை. மீண்டும் முயற்சிக்கவும்!", + speakerErrorNoTest: + "உங்கள் ஸ்பீக்கர்களை நாங்கள் சோதிக்க முடியவில்லை. அவை இயக்கத்தில் உள்ளதா என்பதை உறுதிப்படுத்தவும்!", + allTestsPassed: + "அனைத்து சோதனைகளும் தேர்ச்சி! நீங்கள் கற்றுக்கொள்ளத் தயார்!", + }, +}; + +/** + * Get translations for a specific language + * @param {string} lang - Language code (en, hi, ta, te, ka, kn, tn) + * @returns {object} Translation object for the language, defaults to English + */ +export const getTranslations = (lang) => { + // Handle language code aliases + const langMap = { + tn: "ta", // Map tn to ta (Tamil) + kn: "ka", // Map kn to ka (Kannada) + }; + + const mappedLang = langMap[lang] || lang; + return ( + AUDIO_DIAGNOSTIC_TRANSLATIONS[mappedLang] || + AUDIO_DIAGNOSTIC_TRANSLATIONS.en + ); +}; diff --git a/src/index.css b/src/index.css index 0b962440..556079f5 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,85 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Fix for Letter Hunt game overflow when embedded in Practice */ +.letter-hunt-wrapper > div.h-screen, +.letter-hunt-wrapper > div[class*="h-screen"], +.letter-hunt-wrapper div[class*="h-screen"] { + height: 100% !important; + max-height: 100% !important; + overflow: hidden !important; + position: relative !important; +} + +.letter-hunt-wrapper > div.h-screen > div, +.letter-hunt-wrapper div[class*="h-screen"] > div { + height: 100% !important; + max-height: 100% !important; + overflow: hidden !important; +} + +@layer base { + :root { + --background: 220 30% 98%; + --foreground: 240 10% 15%; + --card: 0 0% 100%; + --card-foreground: 240 10% 15%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 15%; + --primary: 220 85% 60%; + --primary-foreground: 0 0% 100%; + --primary-glow: 220 90% 75%; + --secondary: 35 95% 65%; + --secondary-foreground: 0 0% 100%; + --success: 150 80% 55%; + --success-foreground: 0 0% 100%; + --success-glow: 150 85% 70%; + --warning: 50 100% 70%; + --warning-foreground: 45 100% 15%; + --error: 340 75% 65%; + --error-foreground: 0 0% 100%; + --purple-game: 280 75% 65%; + --pink-game: 320 80% 70%; + --teal-game: 180 70% 55%; + --blue-game: 210 85% 65%; + --green-game: 145 75% 60%; + --orange-game: 25 90% 65%; + --sky-start: 200 100% 85%; + --sky-end: 220 100% 70%; + --muted: 220 20% 94%; + --muted-foreground: 240 10% 40%; + --accent: 220 80% 96%; + --accent-foreground: 220 80% 25%; + --destructive: 0 75% 65%; + --destructive-foreground: 0 0% 100%; + --border: 220 20% 85%; + --input: 220 20% 85%; + --ring: 220 85% 60%; + --radius: 1.25rem; + --gradient-sky: linear-gradient(135deg, hsl(var(--sky-start)), hsl(var(--sky-end))); + --gradient-rainbow: linear-gradient(90deg, hsl(0 80% 70%), hsl(30 80% 70%), hsl(60 80% 70%), hsl(120 80% 70%), hsl(240 80% 70%), hsl(300 80% 70%)); + --gradient-primary: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--primary-glow))); + --gradient-success: linear-gradient(135deg, hsl(var(--success)), hsl(var(--success-glow))); + --gradient-warm: linear-gradient(135deg, hsl(var(--secondary)), hsl(50 90% 75%)); + --gradient-cool: linear-gradient(135deg, hsl(var(--teal-game)), hsl(var(--blue-game))); + --gradient-playful: linear-gradient(135deg, hsl(var(--purple-game)), hsl(var(--pink-game))); + --shadow-magical: 0 12px 40px -8px hsl(var(--primary) / 0.3), 0 0 20px hsl(var(--primary-glow) / 0.2); + --shadow-floating: 0 16px 45px -12px hsl(var(--primary) / 0.25); + --shadow-success: 0 12px 35px -8px hsl(var(--success) / 0.4); + --shadow-colorful: 0 8px 32px -6px hsl(320 70% 60% / 0.3); + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + html, body { margin: 0; padding: 0; @@ -5,6 +87,13 @@ html, body { width: 100%; } +/* Prevent body scroll when Letter Hunt is active */ +body.letter-hunt-active { + overflow: hidden !important; + height: 100vh !important; + max-height: 100vh !important; +} + .loader { width: 250px; height: 20px; @@ -205,3 +294,11 @@ html, body { .assesmentCompleteLevelText { transform: rotate(-12deg); } + +/* Override font for Kannada text in Letter Train */ +.kannada-font-override, +.kannada-font-override *, +span.kannada-font-override, +div.kannada-font-override { + font-family: 'Baloo Tamma 2', Quicksand, sans-serif !important; +} diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/en/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/en/introduction.wav new file mode 100644 index 00000000..ddd312d7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/en/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/hi/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/hi/introduction.wav new file mode 100644 index 00000000..0bbf9fea Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/hi/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/kn/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/kn/introduction.wav new file mode 100644 index 00000000..3f6f3c15 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/kn/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/mr/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/mr/introduction.wav new file mode 100644 index 00000000..70371520 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/mr/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/te/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/te/introduction.wav new file mode 100644 index 00000000..bbddb267 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/introduction/te/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration1.wav new file mode 100644 index 00000000..d2474d54 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration2.wav new file mode 100644 index 00000000..647b516c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration3.wav new file mode 100644 index 00000000..1a873a81 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration4.wav new file mode 100644 index 00000000..2a196eb4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration1.wav new file mode 100644 index 00000000..1297820c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration2.wav new file mode 100644 index 00000000..f8b0fbc7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration3.wav new file mode 100644 index 00000000..76ce7a9c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration4.wav new file mode 100644 index 00000000..70c67956 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration1.wav new file mode 100644 index 00000000..e00ef191 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration2.wav new file mode 100644 index 00000000..399c9e66 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration3.wav new file mode 100644 index 00000000..78403737 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration4.wav new file mode 100644 index 00000000..3ff55792 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration1.wav new file mode 100644 index 00000000..e1c724bd Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration2.wav new file mode 100644 index 00000000..b7aae25c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration3.wav new file mode 100644 index 00000000..c88fa819 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration4.wav new file mode 100644 index 00000000..72d240e7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration1.wav new file mode 100644 index 00000000..2e3f9d4c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration2.wav new file mode 100644 index 00000000..dddd2f9a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration3.wav new file mode 100644 index 00000000..783337ea Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration4.wav new file mode 100644 index 00000000..9172a1a7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/letter-hunt/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration1.wav new file mode 100644 index 00000000..5f859023 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration2.wav new file mode 100644 index 00000000..b040c74a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration3.wav new file mode 100644 index 00000000..4ea3637b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration4.wav new file mode 100644 index 00000000..3a3c857b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration1.wav new file mode 100644 index 00000000..15f90ae8 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration2.wav new file mode 100644 index 00000000..6965c4fa Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration3.wav new file mode 100644 index 00000000..797421d6 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration4.wav new file mode 100644 index 00000000..f95402e2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration1.wav new file mode 100644 index 00000000..312e7fc8 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration2.wav new file mode 100644 index 00000000..fb57ccfc Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration3.wav new file mode 100644 index 00000000..eb569522 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration4.wav new file mode 100644 index 00000000..45d55c8a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration1.wav new file mode 100644 index 00000000..a21bef5d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration2.wav new file mode 100644 index 00000000..1a0d7952 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration3.wav new file mode 100644 index 00000000..dfc047f8 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration4.wav new file mode 100644 index 00000000..8613e22a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration1.wav new file mode 100644 index 00000000..ca83ac07 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration2.wav new file mode 100644 index 00000000..67a2fe8f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration3.wav new file mode 100644 index 00000000..faf1d580 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration4.wav new file mode 100644 index 00000000..d5d4fbd2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/memory-challenge/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration1.wav new file mode 100644 index 00000000..db6e3f9f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration2.wav new file mode 100644 index 00000000..3fa339fb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration3.wav new file mode 100644 index 00000000..b762dbbe Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration4.wav new file mode 100644 index 00000000..6a889c4a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration1.wav new file mode 100644 index 00000000..9201368a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration2.wav new file mode 100644 index 00000000..c5e716f7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration3.wav new file mode 100644 index 00000000..7f0282fb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration4.wav new file mode 100644 index 00000000..e843db06 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration1.wav new file mode 100644 index 00000000..c91f934e Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration2.wav new file mode 100644 index 00000000..936cf57d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration3.wav new file mode 100644 index 00000000..33604e51 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration4.wav new file mode 100644 index 00000000..19446352 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration1.wav new file mode 100644 index 00000000..2a5a192a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration2.wav new file mode 100644 index 00000000..e1675d2b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration3.wav new file mode 100644 index 00000000..1ef2360a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration4.wav new file mode 100644 index 00000000..74057904 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration1.wav new file mode 100644 index 00000000..edea6f93 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration2.wav new file mode 100644 index 00000000..bd7b577d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration3.wav new file mode 100644 index 00000000..d71ebfc4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration4.wav new file mode 100644 index 00000000..afab0099 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-letter-games/quick-sight/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration1.wav new file mode 100644 index 00000000..a542bebe Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration2.wav new file mode 100644 index 00000000..37d2b70f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration3.wav new file mode 100644 index 00000000..a7fc32b8 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration4.wav new file mode 100644 index 00000000..0d124281 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration1.wav new file mode 100644 index 00000000..0ff14555 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration2.wav new file mode 100644 index 00000000..1943d4df Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration3.wav new file mode 100644 index 00000000..111e21c9 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration4.wav new file mode 100644 index 00000000..e64ac6de Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration1.wav new file mode 100644 index 00000000..cc7ba349 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration2.wav new file mode 100644 index 00000000..90de8774 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration3.wav new file mode 100644 index 00000000..8abf7853 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration4.wav new file mode 100644 index 00000000..01ed8cbf Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration1.wav new file mode 100644 index 00000000..b2e23a36 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration2.wav new file mode 100644 index 00000000..f210f4b4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration3.wav new file mode 100644 index 00000000..7fd36d0b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration4.wav new file mode 100644 index 00000000..6c44abd7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration1.wav new file mode 100644 index 00000000..3c0a3d8f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration2.wav new file mode 100644 index 00000000..19c07400 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration3.wav new file mode 100644 index 00000000..885b4854 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration4.wav new file mode 100644 index 00000000..a64cc7d8 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/fill-in-blanks/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/en/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/en/introduction.wav new file mode 100644 index 00000000..091753c1 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/en/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/hi/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/hi/introduction.wav new file mode 100644 index 00000000..5031b123 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/hi/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/kn/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/kn/introduction.wav new file mode 100644 index 00000000..2addf721 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/kn/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/mr/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/mr/introduction.wav new file mode 100644 index 00000000..26a3c8f5 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/mr/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/te/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/te/introduction.wav new file mode 100644 index 00000000..37a00b80 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/introduction/te/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration1.wav new file mode 100644 index 00000000..ff281f67 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration2.wav new file mode 100644 index 00000000..3a3bd689 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration3.wav new file mode 100644 index 00000000..0853b6b3 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration4.wav new file mode 100644 index 00000000..523291ef Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration1.wav new file mode 100644 index 00000000..50011d59 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration2.wav new file mode 100644 index 00000000..0dad3f04 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration3.wav new file mode 100644 index 00000000..5e35a61a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration4.wav new file mode 100644 index 00000000..f0a419d2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration1.wav new file mode 100644 index 00000000..ddeb269a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration2.wav new file mode 100644 index 00000000..689f3600 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration3.wav new file mode 100644 index 00000000..b670cccd Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration4.wav new file mode 100644 index 00000000..ee5286a7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration1.wav new file mode 100644 index 00000000..501c2215 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration2.wav new file mode 100644 index 00000000..43e42b90 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration3.wav new file mode 100644 index 00000000..ad76a52f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration4.wav new file mode 100644 index 00000000..84002fcf Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration1.wav new file mode 100644 index 00000000..dd0c8d2c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration2.wav new file mode 100644 index 00000000..c31731bb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration3.wav new file mode 100644 index 00000000..e3c01712 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration4.wav new file mode 100644 index 00000000..2caafd61 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/sentence-builder/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration1.wav new file mode 100644 index 00000000..057c21a3 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration2.wav new file mode 100644 index 00000000..5c629bba Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration3.wav new file mode 100644 index 00000000..a2c8b9e5 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration4.wav new file mode 100644 index 00000000..0d124281 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration1.wav new file mode 100644 index 00000000..ce5bea7e Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration2.wav new file mode 100644 index 00000000..669f9517 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration3.wav new file mode 100644 index 00000000..38fae52b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration4.wav new file mode 100644 index 00000000..e64ac6de Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration1.wav new file mode 100644 index 00000000..c5f12158 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration2.wav new file mode 100644 index 00000000..79b224d2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration3.wav new file mode 100644 index 00000000..65870145 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration4.wav new file mode 100644 index 00000000..01ed8cbf Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration1.wav new file mode 100644 index 00000000..fd4869cc Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration2.wav new file mode 100644 index 00000000..439659ba Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration3.wav new file mode 100644 index 00000000..b935bb79 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration4.wav new file mode 100644 index 00000000..6c44abd7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration1.wav new file mode 100644 index 00000000..e4c75f67 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration2.wav new file mode 100644 index 00000000..19c07400 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration3.wav new file mode 100644 index 00000000..bc38c251 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration4.wav new file mode 100644 index 00000000..d2184bf9 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-sentence-games/true-or-false/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/en/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/en/introduction.wav new file mode 100644 index 00000000..dcc57e2b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/en/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/hi/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/hi/introduction.wav new file mode 100644 index 00000000..57bc6923 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/hi/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/kn/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/kn/introduction.wav new file mode 100644 index 00000000..55e44595 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/kn/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/mr/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/mr/introduction.wav new file mode 100644 index 00000000..92e7ecc4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/mr/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/te/introduction.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/te/introduction.wav new file mode 100644 index 00000000..c50943cc Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/introduction/te/introduction.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration1.wav new file mode 100644 index 00000000..f3d2fb31 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration2.wav new file mode 100644 index 00000000..03b1d1cf Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration3.wav new file mode 100644 index 00000000..62a4fac2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration4.wav new file mode 100644 index 00000000..e1469177 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration1.wav new file mode 100644 index 00000000..97236764 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration2.wav new file mode 100644 index 00000000..99494616 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration3.wav new file mode 100644 index 00000000..771a0dc1 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration4.wav new file mode 100644 index 00000000..725f3485 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration1.wav new file mode 100644 index 00000000..85726faa Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration2.wav new file mode 100644 index 00000000..555b1c4a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration3.wav new file mode 100644 index 00000000..4b8f9a10 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration4.wav new file mode 100644 index 00000000..f1a7a5ca Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration1.wav new file mode 100644 index 00000000..deade34d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration2.wav new file mode 100644 index 00000000..12def20b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration3.wav new file mode 100644 index 00000000..d040b7a5 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration4.wav new file mode 100644 index 00000000..6eca7af4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration1.wav new file mode 100644 index 00000000..2f6a2bde Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration2.wav new file mode 100644 index 00000000..dc7c9602 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration3.wav new file mode 100644 index 00000000..0424dddb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration4.wav new file mode 100644 index 00000000..d8ed1cc4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/picture-words/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/bread.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/bread.wav new file mode 100644 index 00000000..f80586f9 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/bread.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration1.wav new file mode 100644 index 00000000..4415b31c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration2.wav new file mode 100644 index 00000000..2f14278b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration3.wav new file mode 100644 index 00000000..146345f4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration4.wav new file mode 100644 index 00000000..3c596296 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration1.wav new file mode 100644 index 00000000..986961c0 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration2.wav new file mode 100644 index 00000000..26dd42c9 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration3.wav new file mode 100644 index 00000000..d066dad3 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration4.wav new file mode 100644 index 00000000..14582c66 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/bread.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/bread.wav new file mode 100644 index 00000000..03449fdf Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/bread.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration1.wav new file mode 100644 index 00000000..3aa92e85 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration2.wav new file mode 100644 index 00000000..4c67f38e Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration3.wav new file mode 100644 index 00000000..b63b5212 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration4.wav new file mode 100644 index 00000000..e087d1ba Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/narration4.wav differ diff --git "a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/\340\262\252\340\262\225\340\263\215\340\262\267\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/\340\262\252\340\262\225\340\263\215\340\262\267\340\262\277.wav" new file mode 100644 index 00000000..b3742b3d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/kn/\340\262\252\340\262\225\340\263\215\340\262\267\340\262\277.wav" differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/bread.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/bread.wav new file mode 100644 index 00000000..ad8827e9 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/bread.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration1.wav new file mode 100644 index 00000000..4b5c74e8 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration2.wav new file mode 100644 index 00000000..527a1c55 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration3.wav new file mode 100644 index 00000000..fb98e8e5 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration4.wav new file mode 100644 index 00000000..267e00cb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/narration4.wav differ diff --git "a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/\340\244\270\340\244\253\340\244\260\340\244\232\340\244\202\340\244\246.wav" "b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/\340\244\270\340\244\253\340\244\260\340\244\232\340\244\202\340\244\246.wav" new file mode 100644 index 00000000..daebc41f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/mr/\340\244\270\340\244\253\340\244\260\340\244\232\340\244\202\340\244\246.wav" differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/bread.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/bread.wav new file mode 100644 index 00000000..ad72011c Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/bread.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration1.wav new file mode 100644 index 00000000..0a4bf084 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration2.wav new file mode 100644 index 00000000..10ed6d53 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration3.wav new file mode 100644 index 00000000..f07456cb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration4.wav new file mode 100644 index 00000000..3394dbd4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/narration4.wav differ diff --git "a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/\340\260\252\340\260\225\340\261\215\340\260\267\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/\340\260\252\340\260\225\340\261\215\340\260\267\340\260\277.wav" new file mode 100644 index 00000000..7f390243 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/sound-match/te/\340\260\252\340\260\225\340\261\215\340\260\267\340\260\277.wav" differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration1.wav new file mode 100644 index 00000000..b170877e Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration2.wav new file mode 100644 index 00000000..25a83518 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration3.wav new file mode 100644 index 00000000..880d481f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration4.wav new file mode 100644 index 00000000..7ee81209 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/en/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration1.wav new file mode 100644 index 00000000..bb66f576 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration2.wav new file mode 100644 index 00000000..431c03bf Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration3.wav new file mode 100644 index 00000000..f193dc8d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration4.wav new file mode 100644 index 00000000..1875021d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/hi/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration1.wav new file mode 100644 index 00000000..a122f0ff Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration2.wav new file mode 100644 index 00000000..6777f71e Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration3.wav new file mode 100644 index 00000000..27814d17 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration4.wav new file mode 100644 index 00000000..3bc21a7d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/kn/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration1.wav new file mode 100644 index 00000000..f87305ec Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration2.wav new file mode 100644 index 00000000..2db24a5f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration3.wav new file mode 100644 index 00000000..71c92d54 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration4.wav new file mode 100644 index 00000000..d20921f9 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/mr/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration1.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration1.wav new file mode 100644 index 00000000..d11ce39b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration1.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration2.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration2.wav new file mode 100644 index 00000000..dc7c9602 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration2.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration3.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration3.wav new file mode 100644 index 00000000..e65c7ee2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration3.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration4.wav b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration4.wav new file mode 100644 index 00000000..3fd67764 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/combined-word-games/word-detective/te/narration4.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/failure message/en/failure.wav b/src/lib/axl-explorations/public/audio/audio-preview/failure message/en/failure.wav new file mode 100644 index 00000000..2b888fab Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/failure message/en/failure.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/failure message/hi/failure.wav b/src/lib/axl-explorations/public/audio/audio-preview/failure message/hi/failure.wav new file mode 100644 index 00000000..2f7f9294 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/failure message/hi/failure.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/failure message/kn/failure.wav b/src/lib/axl-explorations/public/audio/audio-preview/failure message/kn/failure.wav new file mode 100644 index 00000000..cca21101 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/failure message/kn/failure.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/failure message/mr/failure.wav b/src/lib/axl-explorations/public/audio/audio-preview/failure message/mr/failure.wav new file mode 100644 index 00000000..e4f73eac Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/failure message/mr/failure.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/failure message/te/failure.wav b/src/lib/axl-explorations/public/audio/audio-preview/failure message/te/failure.wav new file mode 100644 index 00000000..a8b1a3bb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/failure message/te/failure.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/success message/en/success.wav b/src/lib/axl-explorations/public/audio/audio-preview/success message/en/success.wav new file mode 100644 index 00000000..ca83414b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/success message/en/success.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/success message/hi/success.wav b/src/lib/axl-explorations/public/audio/audio-preview/success message/hi/success.wav new file mode 100644 index 00000000..9980bd1e Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/success message/hi/success.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/success message/kn/success.wav b/src/lib/axl-explorations/public/audio/audio-preview/success message/kn/success.wav new file mode 100644 index 00000000..9ee7ee4b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/success message/kn/success.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/success message/mr/success.wav b/src/lib/axl-explorations/public/audio/audio-preview/success message/mr/success.wav new file mode 100644 index 00000000..849cbb01 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/success message/mr/success.wav differ diff --git a/src/lib/axl-explorations/public/audio/audio-preview/success message/te/success.wav b/src/lib/axl-explorations/public/audio/audio-preview/success message/te/success.wav new file mode 100644 index 00000000..48a7bff7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/audio-preview/success message/te/success.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/A.wav b/src/lib/axl-explorations/public/audio/english/letter/A.wav new file mode 100644 index 00000000..352d27f7 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/A.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/B.wav b/src/lib/axl-explorations/public/audio/english/letter/B.wav new file mode 100644 index 00000000..9a1a67be Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/B.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/C.wav b/src/lib/axl-explorations/public/audio/english/letter/C.wav new file mode 100644 index 00000000..b10f4e17 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/C.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/D.wav b/src/lib/axl-explorations/public/audio/english/letter/D.wav new file mode 100644 index 00000000..9cf46e23 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/D.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/E.wav b/src/lib/axl-explorations/public/audio/english/letter/E.wav new file mode 100644 index 00000000..c53ef106 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/E.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/F.wav b/src/lib/axl-explorations/public/audio/english/letter/F.wav new file mode 100644 index 00000000..8ec88249 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/F.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/G.wav b/src/lib/axl-explorations/public/audio/english/letter/G.wav new file mode 100644 index 00000000..af5aaaa5 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/G.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/H.wav b/src/lib/axl-explorations/public/audio/english/letter/H.wav new file mode 100644 index 00000000..fadae7c0 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/H.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/I.wav b/src/lib/axl-explorations/public/audio/english/letter/I.wav new file mode 100644 index 00000000..5a508157 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/I.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/J.wav b/src/lib/axl-explorations/public/audio/english/letter/J.wav new file mode 100644 index 00000000..a92817a4 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/J.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/K.wav b/src/lib/axl-explorations/public/audio/english/letter/K.wav new file mode 100644 index 00000000..dcefdd96 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/K.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/L.wav b/src/lib/axl-explorations/public/audio/english/letter/L.wav new file mode 100644 index 00000000..6a59c188 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/L.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/M.wav b/src/lib/axl-explorations/public/audio/english/letter/M.wav new file mode 100644 index 00000000..64a53827 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/M.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/N.wav b/src/lib/axl-explorations/public/audio/english/letter/N.wav new file mode 100644 index 00000000..adc1cd10 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/N.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/O.wav b/src/lib/axl-explorations/public/audio/english/letter/O.wav new file mode 100644 index 00000000..6b4469fd Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/O.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/P.wav b/src/lib/axl-explorations/public/audio/english/letter/P.wav new file mode 100644 index 00000000..e37008ec Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/P.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/Q.wav b/src/lib/axl-explorations/public/audio/english/letter/Q.wav new file mode 100644 index 00000000..b7a4de63 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/Q.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/R.wav b/src/lib/axl-explorations/public/audio/english/letter/R.wav new file mode 100644 index 00000000..3b25cc18 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/R.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/S.wav b/src/lib/axl-explorations/public/audio/english/letter/S.wav new file mode 100644 index 00000000..20ec98ea Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/S.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/T.wav b/src/lib/axl-explorations/public/audio/english/letter/T.wav new file mode 100644 index 00000000..1df25c61 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/T.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/U.wav b/src/lib/axl-explorations/public/audio/english/letter/U.wav new file mode 100644 index 00000000..facd8755 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/U.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/V.wav b/src/lib/axl-explorations/public/audio/english/letter/V.wav new file mode 100644 index 00000000..6ed8f3ad Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/V.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/W.wav b/src/lib/axl-explorations/public/audio/english/letter/W.wav new file mode 100644 index 00000000..1ff5fbf0 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/W.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/X.wav b/src/lib/axl-explorations/public/audio/english/letter/X.wav new file mode 100644 index 00000000..78c8d0e2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/X.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/Y.wav b/src/lib/axl-explorations/public/audio/english/letter/Y.wav new file mode 100644 index 00000000..2860e3bb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/Y.wav differ diff --git a/src/lib/axl-explorations/public/audio/english/letter/Z.wav b/src/lib/axl-explorations/public/audio/english/letter/Z.wav new file mode 100644 index 00000000..3b6daa88 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/english/letter/Z.wav differ diff --git a/src/lib/axl-explorations/public/audio/kannada/README.md b/src/lib/axl-explorations/public/audio/kannada/README.md new file mode 100644 index 00000000..a42604fa --- /dev/null +++ b/src/lib/axl-explorations/public/audio/kannada/README.md @@ -0,0 +1,74 @@ +# Kannada Audio Files + +This directory contains audio files for Kannada language support in the letter games. + +## Directory Structure + +``` +kannada/ +├── letter/ # Individual letter pronunciation files +│ ├── Level 1/ # Basic vowels: ಅ, ಆ, ಇ, ಈ, ಉ, ಊ, ಋ, ಎ, ಏ +│ ├── Level 2/ # Vowel modifiers and consonants: ಅಂ, ಅಃ, ಕ, ಖ, ಗ, ಘ, ಙ, ಚ, ಛ +│ ├── Level 3/ # Consonants: ತ, ಥ, ದ, ಧ, ನ, ಪ, ಫ, ಬ, ಭ +│ ├── Level 4/ # More consonants: ಹ, ಡ, ಢ, ಣ, ಟ, ಠ, ಡ, ಢ +│ ├── Level 5/ # Advanced consonants: ರ, ಫ, ಛ, etc. +│ ├── Level 6/ # Complex syllables: ಜ್ಞ, ರು, ಸ್ನು, ದು +│ ├── Level 7/ # Syllables with matras: ಳಿ, ಸಿ, ಡಿ, ಲ್ಲಿ, ಗಿ, ರೊ, ಸು, ಳು, ಮಾ +│ ├── Level 8/ # More syllables: ದಿ, ವು, ಡು, ವಾ, ಸು, ತಿ, ಗು, ನಿ, ತು +│ ├── Level 9/ # Advanced syllables: ನೆ, ಕಾ, ಕೆ, ಯಾ, ವಿ, ಲು, ಲಿ, ನಾ, ಕು +│ └── Level 10/ # Expert syllables: ಯಿ, ಹಾ, ರಾ, ತೆ, ದೆ, ಹೇ, ತ್ತು, ಕೊ, ಬಾ +└── README.md # This file +``` + +## Audio File Naming Convention + +- Each letter/syllable should have its own `.wav` file +- File names should match the exact Kannada character +- Example: `ಅ.wav`, `ಆ.wav`, `ಕ.wav`, `ಜ್ಞ.wav` + +## Level-wise Content + +### Level 1 (Basic Vowels) +- ಅ, ಆ, ಇ, ಈ, ಉ, ಊ, ಋ, ಎ, ಏ + +### Level 2 (Vowel Modifiers + Basic Consonants) +- ಅಂ, ಅಃ, ಕ, ಖ, ಗ, ಘ, ಙ, ಚ, ಛ + +### Level 3 (Consonants) +- ತ, ಥ, ದ, ಧ, ನ, ಪ, ಫ, ಬ, ಭ + +### Level 4 (More Consonants) +- ಹ, ಡ, ಢ, ಣ, ಟ, ಠ, ಡ, ಢ + +### Level 5 (Advanced Consonants) +- ರ, ಫ, ಛ, etc. + +### Level 6 (Complex Syllables) +- ಜ್ಞ, ರು, ಸ್ನು, ದು + +### Level 7 (Syllables with Matras) +- ಳಿ, ಸಿ, ಡಿ, ಲ್ಲಿ, ಗಿ, ರೊ, ಸು, ಳು, ಮಾ + +### Level 8 (More Syllables) +- ದಿ, ವು, ಡು, ವಾ, ಸು, ತಿ, ಗು, ನಿ, ತು + +### Level 9 (Advanced Syllables) +- ನೆ, ಕಾ, ಕೆ, ಯಾ, ವಿ, ಲು, ಲಿ, ನಾ, ಕು + +### Level 10 (Expert Syllables) +- ಯಿ, ಹಾ, ರಾ, ತೆ, ದೆ, ಹೇ, ತ್ತು, ಕೊ, ಬಾ + +## Usage + +These audio files are used by the Kannada audio manager to provide pronunciation support for letter games including: +- Letter Recognition Game +- Combined Letter Games (Letter Hunt, Quick Sight, Memory Challenge) + +## Audio Quality Guidelines + +- Format: WAV files +- Sample Rate: 44.1 kHz or 48 kHz +- Bit Depth: 16-bit or 24-bit +- Duration: 1-3 seconds per letter/syllable +- Clear pronunciation with native Kannada speaker +- Consistent volume levels across all files diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205.wav" new file mode 100644 index 00000000..e540cc7d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205\340\262\202.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205\340\262\202.wav" new file mode 100644 index 00000000..191ecfa7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205\340\262\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205\340\262\203.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205\340\262\203.wav" new file mode 100644 index 00000000..621e1e44 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\205\340\262\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\206.wav" new file mode 100644 index 00000000..ff68d60d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\207.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\207.wav" new file mode 100644 index 00000000..457606d8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\210.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\210.wav" new file mode 100644 index 00000000..f8e4c119 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\211.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\211.wav" new file mode 100644 index 00000000..7aa42a47 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\211.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\212.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\212.wav" new file mode 100644 index 00000000..9e94ba70 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\212.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\213.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\213.wav" new file mode 100644 index 00000000..cd520044 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\216.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\216.wav" new file mode 100644 index 00000000..9233e8c7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\216.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\217.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\217.wav" new file mode 100644 index 00000000..3438fea9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\217.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\220.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\220.wav" new file mode 100644 index 00000000..9e2c9a56 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\220.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\222.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\222.wav" new file mode 100644 index 00000000..6ed3c327 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\222.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\223.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\223.wav" new file mode 100644 index 00000000..8f44a76a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\223.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\224.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\224.wav" new file mode 100644 index 00000000..2d3a0b06 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\224.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225.wav" new file mode 100644 index 00000000..690a17c9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\262\276.wav" new file mode 100644 index 00000000..4ece13fc Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\201.wav" new file mode 100644 index 00000000..802a7af3 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\203.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\203.wav" new file mode 100644 index 00000000..541b5bfb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\206.wav" new file mode 100644 index 00000000..d1e8a352 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\210.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\210.wav" new file mode 100644 index 00000000..8c6d13df Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\212.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\212.wav" new file mode 100644 index 00000000..32afa496 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\225\340\263\212.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\226.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\226.wav" new file mode 100644 index 00000000..1ad5e185 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\226.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227.wav" new file mode 100644 index 00000000..b7f642ee Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\262\277.wav" new file mode 100644 index 00000000..49e69394 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\201.wav" new file mode 100644 index 00000000..fec0700a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\206.wav" new file mode 100644 index 00000000..52710adb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\214.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\214.wav" new file mode 100644 index 00000000..a358f857 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\227\340\263\214.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\230.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\230.wav" new file mode 100644 index 00000000..5d04140e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\230.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\231.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\231.wav" new file mode 100644 index 00000000..01e3f7ff Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\231.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\232.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\232.wav" new file mode 100644 index 00000000..243d57d3 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\232.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\232\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\232\340\262\277.wav" new file mode 100644 index 00000000..35e0daae Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\232\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\233.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\233.wav" new file mode 100644 index 00000000..9b3f6377 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\233.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\234.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\234.wav" new file mode 100644 index 00000000..22b6bbc2 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\234.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\235.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\235.wav" new file mode 100644 index 00000000..2debd446 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\235.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\236.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\236.wav" new file mode 100644 index 00000000..37decaac Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\236.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\237.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\237.wav" new file mode 100644 index 00000000..d9066182 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\237.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\240.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\240.wav" new file mode 100644 index 00000000..9cae43a8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\240.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241.wav" new file mode 100644 index 00000000..9b2eb703 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241\340\262\277.wav" new file mode 100644 index 00000000..48af2df5 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241\340\263\201.wav" new file mode 100644 index 00000000..bd2ad783 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\241\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\242.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\242.wav" new file mode 100644 index 00000000..e9e96702 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\242.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\243.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\243.wav" new file mode 100644 index 00000000..430f880b Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\243.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244.wav" new file mode 100644 index 00000000..1ad12582 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\262\277.wav" new file mode 100644 index 00000000..caf8144f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\201.wav" new file mode 100644 index 00000000..18a2cdce Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\206.wav" new file mode 100644 index 00000000..2b3a6fd6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\215\340\262\244\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\215\340\262\244\340\263\201.wav" new file mode 100644 index 00000000..d5dfe3bd Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\244\340\263\215\340\262\244\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\245.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\245.wav" new file mode 100644 index 00000000..1305cd83 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\245.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246.wav" new file mode 100644 index 00000000..18958c09 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\262\277.wav" new file mode 100644 index 00000000..f74c70d7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\263\201.wav" new file mode 100644 index 00000000..73e294a8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\263\206.wav" new file mode 100644 index 00000000..0e15fea9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\246\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\247.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\247.wav" new file mode 100644 index 00000000..c3392463 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\247.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250.wav" new file mode 100644 index 00000000..29d2fb22 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\262\276.wav" new file mode 100644 index 00000000..d8dddae7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\262\277.wav" new file mode 100644 index 00000000..8869a3ac Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\200.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\200.wav" new file mode 100644 index 00000000..ccb3f0c2 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\201.wav" new file mode 100644 index 00000000..e378e49c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\206.wav" new file mode 100644 index 00000000..f02f9197 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\213.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\213.wav" new file mode 100644 index 00000000..730a6d1b Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\215\340\262\250\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\215\340\262\250\340\263\201.wav" new file mode 100644 index 00000000..9160abab Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\250\340\263\215\340\262\250\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\252.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\252.wav" new file mode 100644 index 00000000..ae43009e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\252.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\253.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\253.wav" new file mode 100644 index 00000000..b8463ca0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\253.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\254.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\254.wav" new file mode 100644 index 00000000..761a142f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\254.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\254\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\254\340\262\276.wav" new file mode 100644 index 00000000..a010d7a6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\254\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\255.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\255.wav" new file mode 100644 index 00000000..f0197bfd Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\255.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\256.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\256.wav" new file mode 100644 index 00000000..2a795920 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\256.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\256\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\256\340\262\276.wav" new file mode 100644 index 00000000..d537d31f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\256\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257.wav" new file mode 100644 index 00000000..eaf3e57c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\262\276.wav" new file mode 100644 index 00000000..923e93bb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\262\277.wav" new file mode 100644 index 00000000..f806f90c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\263\201.wav" new file mode 100644 index 00000000..aa4a72bf Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\257\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260.wav" new file mode 100644 index 00000000..30d63292 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\262\276.wav" new file mode 100644 index 00000000..b111c4eb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\262\277.wav" new file mode 100644 index 00000000..52004ef6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\201.wav" new file mode 100644 index 00000000..28926dc8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\202.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\202.wav" new file mode 100644 index 00000000..4ee62713 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\206.wav" new file mode 100644 index 00000000..fd114094 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\260\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262.wav" new file mode 100644 index 00000000..e4bb958c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\262\277.wav" new file mode 100644 index 00000000..2f4bc1a0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\263\201.wav" new file mode 100644 index 00000000..45b088b1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\263\215\340\262\262\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\263\215\340\262\262\340\262\277.wav" new file mode 100644 index 00000000..fa01b50b Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\262\340\263\215\340\262\262\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263.wav" new file mode 100644 index 00000000..0a7d911d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\262\277.wav" new file mode 100644 index 00000000..1f18aa1f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\263\201.wav" new file mode 100644 index 00000000..b377e550 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\263\206.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\263\206.wav" new file mode 100644 index 00000000..0b6c8455 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\263\340\263\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265.wav" new file mode 100644 index 00000000..52574dc3 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\262\276.wav" new file mode 100644 index 00000000..6f8d051a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\262\277.wav" new file mode 100644 index 00000000..972da385 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\263\201.wav" new file mode 100644 index 00000000..b1091b49 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\265\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\266.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\266.wav" new file mode 100644 index 00000000..eb062576 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\266.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\267.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\267.wav" new file mode 100644 index 00000000..552aa938 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\267.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270.wav" new file mode 100644 index 00000000..50014985 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270\340\262\277.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270\340\262\277.wav" new file mode 100644 index 00000000..cfad5c54 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270\340\262\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270\340\263\201.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270\340\263\201.wav" new file mode 100644 index 00000000..7212398f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\270\340\263\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271.wav" new file mode 100644 index 00000000..b2f1c7a5 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271\340\262\276.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271\340\262\276.wav" new file mode 100644 index 00000000..b301699e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271\340\262\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271\340\263\207.wav" "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271\340\263\207.wav" new file mode 100644 index 00000000..29dfd3c1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/kannada/letter/\340\262\271\340\263\207.wav" differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/en/feedback1.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/en/feedback1.wav new file mode 100644 index 00000000..94e08ca2 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/en/feedback1.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/en/feedback2.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/en/feedback2.wav new file mode 100644 index 00000000..c6cde852 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/en/feedback2.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/hi/feedback1.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/hi/feedback1.wav new file mode 100644 index 00000000..3f406386 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/hi/feedback1.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/hi/feedback2.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/hi/feedback2.wav new file mode 100644 index 00000000..47f0b462 Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/hi/feedback2.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/kn/feedback1.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/kn/feedback1.wav new file mode 100644 index 00000000..5d88f3eb Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/kn/feedback1.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/kn/feedback2.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/kn/feedback2.wav new file mode 100644 index 00000000..9caebc9a Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/kn/feedback2.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/mr/feedback1.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/mr/feedback1.wav new file mode 100644 index 00000000..1ea518db Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/mr/feedback1.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/mr/feedback2.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/mr/feedback2.wav new file mode 100644 index 00000000..152a101d Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/mr/feedback2.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/te/feedback1.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/te/feedback1.wav new file mode 100644 index 00000000..2508414f Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/te/feedback1.wav differ diff --git a/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/te/feedback2.wav b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/te/feedback2.wav new file mode 100644 index 00000000..b958e02b Binary files /dev/null and b/src/lib/axl-explorations/public/audio/letter-hunt-incorrect-message/te/feedback2.wav differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205.wav" new file mode 100644 index 00000000..8a45d798 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205\340\244\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205\340\244\202.wav" new file mode 100644 index 00000000..daf33b8a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205\340\244\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205\340\244\203.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205\340\244\203.wav" new file mode 100644 index 00000000..5b940a2f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\205\340\244\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\206.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\206.wav" new file mode 100644 index 00000000..577f039c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\207.wav" new file mode 100644 index 00000000..8c4b83bf Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\210.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\210.wav" new file mode 100644 index 00000000..d41753e0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\211.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\211.wav" new file mode 100644 index 00000000..77606062 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\211.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\212.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\212.wav" new file mode 100644 index 00000000..d5b3e950 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\212.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\213.wav" new file mode 100644 index 00000000..9c656730 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\217.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\217.wav" new file mode 100644 index 00000000..89c0cde1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\217.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\220.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\220.wav" new file mode 100644 index 00000000..2661f140 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\220.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\223.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\223.wav" new file mode 100644 index 00000000..a41542ee Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\223.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\224.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\224.wav" new file mode 100644 index 00000000..5f54782a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\224.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225.wav" new file mode 100644 index 00000000..9fe7d48e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\202.wav" new file mode 100644 index 00000000..3cdd74a0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\276.wav" new file mode 100644 index 00000000..61baab7a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\277.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\277.wav" new file mode 100644 index 00000000..fa9e44c9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\244\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\200.wav" new file mode 100644 index 00000000..ef2220c4 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\201.wav" new file mode 100644 index 00000000..c61de644 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\202.wav" new file mode 100644 index 00000000..b77e2bcd Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\203.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\203.wav" new file mode 100644 index 00000000..e591c682 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\207.wav" new file mode 100644 index 00000000..c0b55aff Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\210.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\210.wav" new file mode 100644 index 00000000..3cd2339f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\213.wav" new file mode 100644 index 00000000..96c539ae Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\214.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\214.wav" new file mode 100644 index 00000000..0f697c2e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\214.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\215\340\244\267.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\215\340\244\267.wav" new file mode 100644 index 00000000..8bdadbcd Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\225\340\245\215\340\244\267.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\226.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\226.wav" new file mode 100644 index 00000000..bc1092c8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\226.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227.wav" new file mode 100644 index 00000000..68076412 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\202.wav" new file mode 100644 index 00000000..ab47befe Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\276.wav" new file mode 100644 index 00000000..572d920f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\277.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\277.wav" new file mode 100644 index 00000000..10cf5a2d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\244\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\200.wav" new file mode 100644 index 00000000..f1ee88c2 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\201.wav" new file mode 100644 index 00000000..c858cdaa Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\202.wav" new file mode 100644 index 00000000..9e396f56 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\203.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\203.wav" new file mode 100644 index 00000000..a9af309f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\207.wav" new file mode 100644 index 00000000..301f5821 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\210.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\210.wav" new file mode 100644 index 00000000..c217cf82 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\213.wav" new file mode 100644 index 00000000..c4fba3c6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\214.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\214.wav" new file mode 100644 index 00000000..4807c3d2 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\227\340\245\214.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\230.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\230.wav" new file mode 100644 index 00000000..76d62761 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\230.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\232.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\232.wav" new file mode 100644 index 00000000..1bf567dd Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\232.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\233.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\233.wav" new file mode 100644 index 00000000..9bec0e88 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\233.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\234.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\234.wav" new file mode 100644 index 00000000..d18b66e7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\234.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\234\340\245\215\340\244\236.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\234\340\245\215\340\244\236.wav" new file mode 100644 index 00000000..64f54c7b Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\234\340\245\215\340\244\236.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\235.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\235.wav" new file mode 100644 index 00000000..2ce24d79 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\235.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\237.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\237.wav" new file mode 100644 index 00000000..34d5697d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\237.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\240.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\240.wav" new file mode 100644 index 00000000..04952b19 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\240.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\241.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\241.wav" new file mode 100644 index 00000000..4e4efa71 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\241.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\242.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\242.wav" new file mode 100644 index 00000000..7c39992a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\242.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\243.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\243.wav" new file mode 100644 index 00000000..40af0007 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\243.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244.wav" new file mode 100644 index 00000000..07dab10a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\244\276.wav" new file mode 100644 index 00000000..a95fcf70 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\244\277.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\244\277.wav" new file mode 100644 index 00000000..09a2701d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\244\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\200.wav" new file mode 100644 index 00000000..ccae26b8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\201.wav" new file mode 100644 index 00000000..13d6e3a7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\202.wav" new file mode 100644 index 00000000..4833785f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\207.wav" new file mode 100644 index 00000000..9710ec2f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\210.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\210.wav" new file mode 100644 index 00000000..e5c141df Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\213.wav" new file mode 100644 index 00000000..ec35b03d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\244\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\245.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\245.wav" new file mode 100644 index 00000000..f5e5a951 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\245.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\246.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\246.wav" new file mode 100644 index 00000000..78aee197 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\246.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\247.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\247.wav" new file mode 100644 index 00000000..ad69ac70 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\247.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250.wav" new file mode 100644 index 00000000..286c4484 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\244\276.wav" new file mode 100644 index 00000000..e1e16590 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\244\277.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\244\277.wav" new file mode 100644 index 00000000..60801d7a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\244\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\200.wav" new file mode 100644 index 00000000..07d19100 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\201.wav" new file mode 100644 index 00000000..8bb41e4a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\202.wav" new file mode 100644 index 00000000..8969e86e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\207.wav" new file mode 100644 index 00000000..b3dd985a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\210.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\210.wav" new file mode 100644 index 00000000..798c652d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\213.wav" new file mode 100644 index 00000000..f5725b0f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\250\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\252.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\252.wav" new file mode 100644 index 00000000..93e89364 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\252.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\253.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\253.wav" new file mode 100644 index 00000000..5207fe0b Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\253.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\254.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\254.wav" new file mode 100644 index 00000000..86c30390 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\254.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\255.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\255.wav" new file mode 100644 index 00000000..fcb57a95 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\255.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256.wav" new file mode 100644 index 00000000..517f9088 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\244\276.wav" new file mode 100644 index 00000000..bcae5c0a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\244\277.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\244\277.wav" new file mode 100644 index 00000000..f9e83796 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\244\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\200.wav" new file mode 100644 index 00000000..3b89f18d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\201.wav" new file mode 100644 index 00000000..aa1acd12 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\202.wav" new file mode 100644 index 00000000..93fc2992 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\207.wav" new file mode 100644 index 00000000..b4acb297 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\210.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\210.wav" new file mode 100644 index 00000000..00684ba1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\213.wav" new file mode 100644 index 00000000..8a206ad2 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\256\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\257.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\257.wav" new file mode 100644 index 00000000..a7c18e67 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\257.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\257\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\257\340\244\276.wav" new file mode 100644 index 00000000..1ccc0fe0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\257\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260.wav" new file mode 100644 index 00000000..0346071d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\244\276.wav" new file mode 100644 index 00000000..cf7b7840 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\200.wav" new file mode 100644 index 00000000..ee55ffcf Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\201.wav" new file mode 100644 index 00000000..b2926a26 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\202.wav" new file mode 100644 index 00000000..ae988583 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\207.wav" new file mode 100644 index 00000000..ff86aa0b Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\213.wav" new file mode 100644 index 00000000..6bff5c73 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\260\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262.wav" new file mode 100644 index 00000000..40355751 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\244\276.wav" new file mode 100644 index 00000000..18b4ca06 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\200.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\200.wav" new file mode 100644 index 00000000..5b148356 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\201.wav" new file mode 100644 index 00000000..da0ae670 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\202.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\202.wav" new file mode 100644 index 00000000..7e9e28f5 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\207.wav" new file mode 100644 index 00000000..c47a5276 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\213.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\213.wav" new file mode 100644 index 00000000..dbc7fc7f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\262\340\245\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\263.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\263.wav" new file mode 100644 index 00000000..aaef8fb1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\263.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\265.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\265.wav" new file mode 100644 index 00000000..7ab4ce9a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\265.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\265\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\265\340\244\276.wav" new file mode 100644 index 00000000..00a5476d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\265\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\266.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\266.wav" new file mode 100644 index 00000000..a4d9885f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\266.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\267.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\267.wav" new file mode 100644 index 00000000..b41bc185 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\267.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270.wav" new file mode 100644 index 00000000..d762eb4e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\244\276.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\244\276.wav" new file mode 100644 index 00000000..4ae96c47 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\244\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\245\201.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\245\201.wav" new file mode 100644 index 00000000..b669ded4 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\245\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\245\207.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\245\207.wav" new file mode 100644 index 00000000..f3070f8c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\270\340\245\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\271.wav" "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\271.wav" new file mode 100644 index 00000000..3e8fb905 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/marathi/letter/\340\244\271.wav" differ diff --git a/src/lib/axl-explorations/public/audio/telugu/README.md b/src/lib/axl-explorations/public/audio/telugu/README.md new file mode 100644 index 00000000..dcf94f3e --- /dev/null +++ b/src/lib/axl-explorations/public/audio/telugu/README.md @@ -0,0 +1,150 @@ +# Telugu Audio Files Setup + +## 📁 Audio Folder Location +Place your Telugu audio files in: `public/audio/telugu/` + +## 🎵 File Naming Convention +Name your audio files exactly as the Telugu letters appear in the game: + +### Level 1 Files: +- `అ.wav` +- `ఆ.wav` +- `ఉ.wav` +- `ఎ.wav` +- `ఏ.wav` +- `ఇ.wav` +- `ఒ.wav` +- `అం.wav` +- `ఈ.wav` +- `ఊ.wav` +- `ఓ.wav` + +### Level 2 Files: +- `ఐ.wav` +- `ఔ.wav` +- `ఋ.wav` +- `న.wav` +- `ల.wav` +- `క.wav` +- `ర.wav` +- `ప.wav` +- `త.wav` +- `ద.wav` + +### Level 3 Files: +- `మ.wav` +- `వ.wav` +- `డ.wav` +- `చ.wav` +- `గ.wav` +- `ట.wav` +- `స.wav` +- `య.wav` +- `బ.wav` +- `జ.wav` + +### Level 4 Files: +- `ష.wav` +- `శ.wav` +- `ళ.wav` +- `ణ.wav` +- `హ.wav` +- `ధ.wav` +- `భ.wav` +- `థ.wav` +- `ఠ.wav` +- `ఖ.wav` + +### Level 5 Files: +- `ఫ.wav` +- `ఘ.wav` +- `ఞ.wav` +- `ఛ.wav` +- `ఢ.wav` +- `ఝ.wav` +- `ఱ.wav` +- `లు.wav` +- `ని.wav` +- `ది.wav` + +### Level 6 Files: +- `ను.wav` +- `కు.wav` +- `లో.wav` +- `డి.wav` +- `రా.wav` +- `వా.wav` +- `గా.wav` +- `దా.wav` +- `రు.wav` +- `డు.wav` + +### Level 7 Files: +- `రి.wav` +- `గు.wav` +- `కా.wav` +- `చి.wav` +- `చేద.wav` +- `చె.wav` +- `పా.wav` +- `వియి.wav` +- `కి.wav` +- `దిు.wav` + +### Level 8 Files: +- `మా.wav` +- `లి.wav` +- `నా.wav` +- `వు.wav` +- `తో.wav` +- `న్నా.wav` +- `టి.wav` +- `పు.wav` +- `ము.wav` +- `సి.wav` + +### Level 9 Files: +- `యి.wav` +- `తి.wav` +- `నే.wav` +- `తా.wav` +- `తు.wav` +- `తె.wav` +- `పి.wav` +- `చూ.wav` +- `డా.wav` +- `కొ.wav` + +### Level 10 Files: +- `మీ.wav` +- `పో.wav` +- `సు.wav` +- `కిం.wav` +- `మై.wav` +- `కృ.wav` +- `గౌ.wav` + +## 🔧 How It Works + +1. **Automatic Detection**: The game automatically detects if audio files exist for each letter +2. **Fallback System**: If an audio file is missing, the game falls back to text-to-speech +3. **No Configuration Needed**: Just add the files to the folder and they'll be used automatically + +## 📝 Notes + +- **File Format**: Currently supports `.wav` files +- **File Size**: Keep files reasonably sized for web performance +- **Quality**: Use clear, high-quality audio for best learning experience +- **Missing Files**: If a file is missing, the game will use text-to-speech as fallback + +## 🚀 Quick Start + +1. Create the folder: `public/audio/telugu/` +2. Add your `.wav` files with exact letter names +3. Start the game - audio will work automatically! + +## 🔍 Troubleshooting + +- **Audio not playing**: Check file names match exactly (including special characters) +- **File not found**: Ensure files are in `public/audio/telugu/` folder +- **Wrong pronunciation**: Verify file names match the Telugu letters exactly diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205.wav" new file mode 100644 index 00000000..c2866732 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205\340\260\202.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205\340\260\202.wav" new file mode 100644 index 00000000..064c2ba7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205\340\260\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205\340\260\203.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205\340\260\203.wav" new file mode 100644 index 00000000..1f403c6c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\205\340\260\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\206.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\206.wav" new file mode 100644 index 00000000..eb9ab22f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\207.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\207.wav" new file mode 100644 index 00000000..429b17cb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\210.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\210.wav" new file mode 100644 index 00000000..6df96dfc Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\211.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\211.wav" new file mode 100644 index 00000000..701bf250 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\211.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\212.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\212.wav" new file mode 100644 index 00000000..f6938e65 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\212.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\213.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\213.wav" new file mode 100644 index 00000000..db7d1bb0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\216.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\216.wav" new file mode 100644 index 00000000..1533fc07 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\216.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\217.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\217.wav" new file mode 100644 index 00000000..8e62f9a6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\217.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\220.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\220.wav" new file mode 100644 index 00000000..e39f35c1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\220.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\222.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\222.wav" new file mode 100644 index 00000000..b7b8d8cc Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\222.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\223.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\223.wav" new file mode 100644 index 00000000..77b8d5ae Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\223.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\224.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\224.wav" new file mode 100644 index 00000000..bf097750 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\224.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225.wav" new file mode 100644 index 00000000..58109a61 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\276.wav" new file mode 100644 index 00000000..67c80fe6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\277.wav" new file mode 100644 index 00000000..82923144 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\277\340\260\202.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\277\340\260\202.wav" new file mode 100644 index 00000000..b9822702 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\260\277\340\260\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\201.wav" new file mode 100644 index 00000000..1e416206 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\203.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\203.wav" new file mode 100644 index 00000000..fec10884 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\203.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\212.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\212.wav" new file mode 100644 index 00000000..70fc3a6d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\212.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\215\340\260\267.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\215\340\260\267.wav" new file mode 100644 index 00000000..f7509816 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\225\340\261\215\340\260\267.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\226.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\226.wav" new file mode 100644 index 00000000..10a4cab1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\226.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227.wav" new file mode 100644 index 00000000..efee34f1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\260\276.wav" new file mode 100644 index 00000000..965b4d13 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\261\201.wav" new file mode 100644 index 00000000..49f74bb4 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\261\214.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\261\214.wav" new file mode 100644 index 00000000..0b75652f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\227\340\261\214.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\230.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\230.wav" new file mode 100644 index 00000000..4fc60ead Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\230.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\231.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\231.wav" new file mode 100644 index 00000000..8858c6dc Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\231.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232.wav" new file mode 100644 index 00000000..edbe10a9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\260\277.wav" new file mode 100644 index 00000000..b6d7f6c9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\202.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\202.wav" new file mode 100644 index 00000000..dab83b85 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\202.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\206.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\206.wav" new file mode 100644 index 00000000..4be99768 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\207.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\207.wav" new file mode 100644 index 00000000..b4211a7c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\232\340\261\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\233.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\233.wav" new file mode 100644 index 00000000..30a7f3a0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\233.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\234.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\234.wav" new file mode 100644 index 00000000..3b6e08f9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\234.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\235.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\235.wav" new file mode 100644 index 00000000..92d9c035 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\235.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\236.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\236.wav" new file mode 100644 index 00000000..6116f1f0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\236.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\237.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\237.wav" new file mode 100644 index 00000000..913216c9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\237.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\237\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\237\340\260\277.wav" new file mode 100644 index 00000000..8bca8d46 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\237\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\240.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\240.wav" new file mode 100644 index 00000000..d7c8438c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\240.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241.wav" new file mode 100644 index 00000000..28236d1d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\260\276.wav" new file mode 100644 index 00000000..abea7986 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\260\277.wav" new file mode 100644 index 00000000..40e71be5 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\261\201.wav" new file mode 100644 index 00000000..693a5439 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\241\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\242.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\242.wav" new file mode 100644 index 00000000..9d65a2f0 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\242.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\243.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\243.wav" new file mode 100644 index 00000000..38a1ecd8 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\243.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244.wav" new file mode 100644 index 00000000..e64f74f7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\260\276.wav" new file mode 100644 index 00000000..5ef7c9a5 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\260\277.wav" new file mode 100644 index 00000000..b202bdd7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\201.wav" new file mode 100644 index 00000000..8089f6a1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\206.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\206.wav" new file mode 100644 index 00000000..616a8141 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\206.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\213.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\213.wav" new file mode 100644 index 00000000..1b16bb27 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\244\340\261\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\245.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\245.wav" new file mode 100644 index 00000000..492d10d9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\245.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246.wav" new file mode 100644 index 00000000..67e124f9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\260\276.wav" new file mode 100644 index 00000000..6ae30a78 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\260\277.wav" new file mode 100644 index 00000000..d04aa1e6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\261\201.wav" new file mode 100644 index 00000000..9f2a49b6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\246\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\247.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\247.wav" new file mode 100644 index 00000000..5a40efb3 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\247.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250.wav" new file mode 100644 index 00000000..48a9e00a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\260\276.wav" new file mode 100644 index 00000000..6abf965e Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\260\277.wav" new file mode 100644 index 00000000..e40bb02d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\201.wav" new file mode 100644 index 00000000..ddb22f47 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\207.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\207.wav" new file mode 100644 index 00000000..f61487c3 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\207.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\215\340\260\250\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\215\340\260\250\340\260\276.wav" new file mode 100644 index 00000000..1574cd45 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\250\340\261\215\340\260\250\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252.wav" new file mode 100644 index 00000000..bccd051a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\260\276.wav" new file mode 100644 index 00000000..3cac85f2 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\260\277.wav" new file mode 100644 index 00000000..c38d52eb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\261\201.wav" new file mode 100644 index 00000000..4fe98f3d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\261\213.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\261\213.wav" new file mode 100644 index 00000000..2a7525d9 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\252\340\261\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\253.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\253.wav" new file mode 100644 index 00000000..c6e8336a Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\253.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\254.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\254.wav" new file mode 100644 index 00000000..a3e59432 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\254.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\255.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\255.wav" new file mode 100644 index 00000000..d5a0d582 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\255.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256.wav" new file mode 100644 index 00000000..94ca70cc Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\260\276.wav" new file mode 100644 index 00000000..0fc77825 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\200.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\200.wav" new file mode 100644 index 00000000..f901b91c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\200.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\201.wav" new file mode 100644 index 00000000..19354c3f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\210.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\210.wav" new file mode 100644 index 00000000..d9d67c2f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\256\340\261\210.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\257.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\257.wav" new file mode 100644 index 00000000..3fcae783 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\257.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\257\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\257\340\260\277.wav" new file mode 100644 index 00000000..1658aedf Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\257\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260.wav" new file mode 100644 index 00000000..a61cd5b1 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\260\276.wav" new file mode 100644 index 00000000..a7c52b7d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\260\277.wav" new file mode 100644 index 00000000..8abf02b6 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\261\201.wav" new file mode 100644 index 00000000..d657223c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\260\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\261.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\261.wav" new file mode 100644 index 00000000..7ad98082 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\261.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262.wav" new file mode 100644 index 00000000..4114d2e7 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\260\277.wav" new file mode 100644 index 00000000..2cf31862 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\261\201.wav" new file mode 100644 index 00000000..5cf7d0ac Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\261\213.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\261\213.wav" new file mode 100644 index 00000000..aa958208 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\262\340\261\213.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\263.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\263.wav" new file mode 100644 index 00000000..41f7f4af Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\263.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265.wav" new file mode 100644 index 00000000..e658fb39 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\260\276.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\260\276.wav" new file mode 100644 index 00000000..eb8fd78f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\260\276.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\260\277.wav" new file mode 100644 index 00000000..4324d2eb Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\261\201.wav" new file mode 100644 index 00000000..1867b43d Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\265\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\266.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\266.wav" new file mode 100644 index 00000000..5248b53f Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\266.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\267.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\267.wav" new file mode 100644 index 00000000..b120366c Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\267.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270.wav" new file mode 100644 index 00000000..8be821fc Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270\340\260\277.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270\340\260\277.wav" new file mode 100644 index 00000000..037ce048 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270\340\260\277.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270\340\261\201.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270\340\261\201.wav" new file mode 100644 index 00000000..478e1c68 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\270\340\261\201.wav" differ diff --git "a/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\271.wav" "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\271.wav" new file mode 100644 index 00000000..b9763374 Binary files /dev/null and "b/src/lib/axl-explorations/public/audio/telugu/letter/\340\260\271.wav" differ diff --git a/src/lib/axl-explorations/src/components/Achievements.tsx b/src/lib/axl-explorations/src/components/Achievements.tsx new file mode 100644 index 00000000..b28b3a93 --- /dev/null +++ b/src/lib/axl-explorations/src/components/Achievements.tsx @@ -0,0 +1,113 @@ +import { Badge } from "./ui/badge"; +import { Card } from "./ui/card"; +import { Crown, Zap, BookOpen, Brain, Award, Sparkles } from "lucide-react"; +import { cn } from "../lib/utils"; + +interface Achievement { + id: string; + title: string; + description: string; + icon: React.ComponentType; + color: string; + isEarned: boolean; +} + +const achievements: Achievement[] = [ + { + id: "learning-champion", + title: "Learning Champion", + description: "Complete your first game!", + icon: Crown, + color: "text-warning", + isEarned: true + }, + { + id: "quick-learner", + title: "Quick Learner", + description: "Answer 5 questions in under 10 seconds", + icon: Zap, + color: "text-blue-game", + isEarned: true + }, + + { + id: "letter-master", + title: "Letter Master", + description: "Complete 10 letter games", + icon: Brain, + color: "text-purple-game", + isEarned: true + }, + { + id: "perfect-week", + title: "Perfect Week", + description: "Play every day for a week", + icon: Award, + color: "text-error", + isEarned: false + }, + { + id: "star-collector", + title: "Star Collector", + description: "Earn 100 stars", + icon: Sparkles, + color: "text-pink-game", + isEarned: false + } +]; + +interface AchievementsProps { + className?: string; + maxDisplay?: number; +} + +export function Achievements({ className, maxDisplay = 3 }: AchievementsProps) { + const recentAchievements = achievements + .filter(achievement => achievement.isEarned) + .slice(0, maxDisplay); + + return ( + +
+
+ +
+

Recent Achievements

+
+ +
+ {recentAchievements.map((achievement, index) => ( +
+
+
+ +
+
+
+ {achievement.title} +
+
+ {achievement.description} +
+
+
+ + + Earned! + +
+ ))} +
+ + {recentAchievements.length === 0 && ( +
+ +

Start playing to earn achievements!

+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/AudioButton.tsx b/src/lib/axl-explorations/src/components/AudioButton.tsx new file mode 100644 index 00000000..3b57bf29 --- /dev/null +++ b/src/lib/axl-explorations/src/components/AudioButton.tsx @@ -0,0 +1,117 @@ +import { useState } from "react"; +import { Button } from "./ui/button"; +import { Volume2, VolumeX } from "lucide-react"; +import { cn } from "../lib/utils"; + +interface AudioButtonProps { + audioText: string; + language?: string; // Add language support + className?: string; + variant?: "default" | "outline" | "ghost"; + size?: "default" | "sm" | "lg"; +} + +export function AudioButton({ audioText, language, className, variant = "outline", size = "default" }: AudioButtonProps) { + const [isPlaying, setIsPlaying] = useState(false); + + const handlePlay = () => { + setIsPlaying(true); + + // Cancel any ongoing speech + speechSynthesis.cancel(); + + // Create enhanced speech synthesis with language-specific settings + const utterance = new SpeechSynthesisUtterance(audioText); + + // ✅ UPDATED: Language-specific voice settings with improved speed and clarity + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 0.8; // ✅ FASTER: More natural speed for Telugu + utterance.pitch = 0.8; // ✅ LOWER: More natural for Telugu + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 0.8; // ✅ FASTER: More natural speed for Marathi + utterance.pitch = 0.8; // ✅ LOWER: More natural for Marathi + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'en': + default: + utterance.lang = 'en-IN'; + utterance.rate = 0.9; // ✅ FASTER: More natural speed for English + utterance.pitch = 1.0; + utterance.volume = 0.9; + break; + } + + // Enhanced voice selection with fallbacks + const voices = speechSynthesis.getVoices(); + let selectedVoice = null; + + // First try: Exact language match + selectedVoice = voices.find(voice => voice.lang === utterance.lang); + + // Second try: Language family match (e.g., 'te' for 'te-IN') + if (!selectedVoice && language) { + const langCode = utterance.lang.split('-')[0]; + selectedVoice = voices.find(voice => voice.lang.startsWith(langCode)); + } + + // Third try: Regional variations + if (!selectedVoice && language === 'te') { + selectedVoice = voices.find(voice => + voice.lang.includes('te') || voice.lang.includes('Telugu') || + voice.lang.includes('hi-IN') || voice.lang.includes('hi') // Hindi fallback + ); + } + + if (!selectedVoice && language === 'mr') { + selectedVoice = voices.find(voice => + voice.lang.includes('mr') || voice.lang.includes('Marathi') || + voice.lang.includes('hi-IN') || voice.lang.includes('hi') // Hindi fallback + ); + } + + // Apply selected voice if found + if (selectedVoice) { + utterance.voice = selectedVoice; + console.log(`🎤 AudioButton using voice: ${selectedVoice.name} (${selectedVoice.lang}) for ${language}`); + } else { + console.warn(`⚠️ AudioButton: No suitable voice found for ${language}, using default`); + } + + utterance.onend = () => { + setIsPlaying(false); + }; + + utterance.onerror = (event) => { + console.warn('AudioButton speech synthesis error for:', audioText, event); + setIsPlaying(false); + }; + + speechSynthesis.speak(utterance); + }; + + return ( + + ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/ClockwiseTimer.tsx b/src/lib/axl-explorations/src/components/ClockwiseTimer.tsx new file mode 100644 index 00000000..04b154fe --- /dev/null +++ b/src/lib/axl-explorations/src/components/ClockwiseTimer.tsx @@ -0,0 +1,73 @@ +import React from 'react'; + +interface ClockwiseTimerProps { + timeRemaining: number; + totalTime: number; + className?: string; +} + +export function ClockwiseTimer({ timeRemaining, totalTime, className = "" }: ClockwiseTimerProps) { + // Calculate the percentage of time remaining + const percentage = (timeRemaining / totalTime) * 100; + + // Calculate the stroke-dasharray for the circle + const circumference = 2 * Math.PI * 30; // radius = 30 (reduced from 45) + const strokeDasharray = circumference; + const strokeDashoffset = circumference - (percentage / 100) * circumference; + + // Determine color based on time remaining + const getColor = () => { + if (percentage < 30) return '#ef4444'; // red + if (percentage < 60) return '#f59e0b'; // yellow + return '#10b981'; // green + }; + + // Format time as MM:SS or M:SS + const formatTime = (seconds: number) => { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; + }; + + const color = getColor(); + + return ( +
+
+ {/* Circular progress bar without time inside */} +
+ + {/* Background circle */} + + {/* Progress circle */} + + +
+ {/* Time text outside the circle in the same row */} +
+ + {formatTime(timeRemaining)} + +
+
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/CountdownTimer.tsx b/src/lib/axl-explorations/src/components/CountdownTimer.tsx new file mode 100644 index 00000000..152134f5 --- /dev/null +++ b/src/lib/axl-explorations/src/components/CountdownTimer.tsx @@ -0,0 +1,101 @@ +import { useEffect, useState } from "react"; +import { Card } from "./ui/card"; + +interface CountdownTimerProps { + initialCount?: number; + onComplete: () => void; +} + +export function CountdownTimer({ + initialCount = 3, + onComplete, +}: CountdownTimerProps) { + const [count, setCount] = useState(initialCount); + + useEffect(() => { + if (count === 0) { + onComplete(); + return; + } + + const timer = setTimeout(() => { + setCount(count - 1); + }, 1000); + + return () => clearTimeout(timer); + }, [count, onComplete]); + + return ( + +
+ {/* Countdown Number with magical animations */} +
+
+ {/* Multiple pulsing rings for magical effect */} +
+
+
+ + {/* Main countdown circle with gradient - Responsive sizing */} +
+ {/* Inner magical glow */} +
+ + {/* The countdown number - Responsive text */} +
+ {count} +
+
+ + {/* Floating sparkles with different animations - Responsive sizing */} +
+ +
+
+ 💫 +
+
+ +
+
+ 🌟 +
+ + {/* Orbiting small stars - Responsive sizing and positioning */} +
+
+
+
💫
+
🌟
+
+
+
+ + {/* Progress dots with rainbow colors - Responsive sizing */} +
+ {[...Array(3)].map((_, index) => { + const colors = [ + 'from-blue-500 to-purple-500', + 'from-purple-500 to-pink-500', + 'from-pink-500 to-red-500' + ]; + return ( +
+ ); + })} +
+
+
+ ); +} + diff --git a/src/lib/axl-explorations/src/components/DemoCompletionScreen.tsx b/src/lib/axl-explorations/src/components/DemoCompletionScreen.tsx new file mode 100644 index 00000000..ec1a26bf --- /dev/null +++ b/src/lib/axl-explorations/src/components/DemoCompletionScreen.tsx @@ -0,0 +1,146 @@ +import { Button } from "./ui/button"; +import { Card } from "./ui/card"; +import { CheckCircle, Gamepad2, RotateCcw, ArrowLeft } from "lucide-react"; + +/** + * DemoCompletionScreen - A reusable completion screen for game demos + * + * @example + * ```tsx + * // In your game preview component: + * if (previewPhase === 'completion') { + * return ( + * onStartGame()} + * onReplayDemo={() => setPreviewPhase('countdown')} + * onBack={handleBack} + * hideHeader={false} + * totalDemos={3} + * /> + * ); + * } + * ``` + * + * @param language - The current language code ('en', 'te', 'kn', 'mr') + * @param onStartGame - Callback when "Start Game" button is clicked + * @param onReplayDemo - Callback when "Replay Demo" button is clicked + * @param onBack - Optional callback for the back button + * @param hideHeader - Whether to hide the header/back button + * @param totalDemos - Total number of demos (for tracking purposes) + */ + +interface DemoCompletionScreenProps { + language: string; + onStartGame: () => void; + onReplayDemo: () => void; + onBack?: () => void; + hideHeader?: boolean; + totalDemos?: number; +} + +export function DemoCompletionScreen({ + language, + onStartGame, + onReplayDemo, + onBack, + hideHeader = false, + totalDemos = 3 +}: DemoCompletionScreenProps) { + + const translations = { + en: { + title: 'Demo Complete!', + description: 'You\'ve completed the demo. Ready to play?', + startGame: 'Start Game', + replayDemo: 'Replay Demo', + back: 'Back' + }, + te: { + title: 'డెమో పూర్తి!', + description: 'మీరు డెమోను పూర్తి చేశారు. ఆడటానికి సిద్ధంగా ఉన్నారా?', + startGame: 'గేమ్ ప్రారంభించండి', + replayDemo: 'డెమోను మళ్లీ ఆడండి', + back: 'వెనుకకు' + }, + kn: { + title: 'ಡೆಮೊ ಪೂರ್ಣವಾಗಿದೆ!', + description: 'ನೀವು ಡೆಮೊವನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ. ಆಡಲು ಸಿದ್ಧರಿದ್ದೀರಾ?', + startGame: 'ಆಟವನ್ನು ಪ್ರಾರಂಭಿಸಿ', + replayDemo: 'ಡೆಮೊವನ್ನು ಮತ್ತೆ ಆಡಿ', + back: 'ಹಿಂದಕ್ಕೆ' + }, + mr: { + title: 'डेमो पूर्ण झाले!', + description: 'तुम्ही डेमो पूर्ण केला आहे. खेळण्यासाठी तयार आहात?', + startGame: 'गेम सुरू करा', + replayDemo: 'डेमो पुन्हा खेळा', + back: 'मागे' + } + }; + + const t = translations[language as keyof typeof translations] || translations.en; + + return ( +
+
+ {!hideHeader && onBack && ( +
+ +
+ )} + +
+ +
+
+ +
+ +
+

+ {t.title} +

+

+ {t.description} +

+
+ +
+ + + + + +
+ +
+
+
+
+
+ ); +} + diff --git a/src/lib/axl-explorations/src/components/DetectiveBackground.tsx b/src/lib/axl-explorations/src/components/DetectiveBackground.tsx new file mode 100644 index 00000000..41cc9ea4 --- /dev/null +++ b/src/lib/axl-explorations/src/components/DetectiveBackground.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +interface DetectiveBackgroundProps { + children: React.ReactNode; + className?: string; +} + +export function DetectiveBackground({ children, className = '' }: DetectiveBackgroundProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/lib/axl-explorations/src/components/FuelProgressBar.tsx b/src/lib/axl-explorations/src/components/FuelProgressBar.tsx new file mode 100644 index 00000000..07c0906e --- /dev/null +++ b/src/lib/axl-explorations/src/components/FuelProgressBar.tsx @@ -0,0 +1,157 @@ +import { Fuel } from "lucide-react"; +import { cn } from "../lib/utils"; + +interface FuelProgressBarProps { + currentFuel: number; + requiredFuel: number; + className?: string; + showRocket?: boolean; + hidePercentage?: boolean; + fuelIconImage?: string; + showCheckpoint?: boolean; + maxFuel: number; + hideHeader?: boolean; + progressIcon?: string; // Custom icon to show on progress bar (default: 🚀) +} + + +export function FuelProgressBar({ + currentFuel, + requiredFuel, + className, + showRocket = true, + hidePercentage = false, + fuelIconImage, + showCheckpoint = true, + maxFuel, + hideHeader = false, + progressIcon = "🚀" +}: FuelProgressBarProps) { + const percentage = Math.min((currentFuel / maxFuel) * 100, 100); + const isComplete = currentFuel >= requiredFuel; + + const progressColor = currentFuel <= requiredFuel/2 ? "bg-orange-500" : currentFuel <= requiredFuel ? "bg-blue-500" : "bg-green-500"; + + return ( +
+ {!hideHeader && ( +
+
+ {fuelIconImage ? ( + Fuel + ) : ( + + )} + + Fuel: {currentFuel} / {maxFuel} + +
+ {showRocket && isComplete && ( +
+ Ready to Launch! +
+ )} +
+ )} + +
+ {/* Neutral background */} +
+ + {/* Progress fill - color changes based on zone */} +
+
+ {/* Sparkles with random positions across the progress bar fill */} + {percentage > 0 && ( + <> + {[...Array(40)].map((_, i) => { + // Use seeded random-like values based on index for consistent but random-looking positions + const seed1 = Math.sin(i * 12.9898) * 43758.5453; + const seed2 = Math.sin(i * 78.233) * 43758.5453; + const seed3 = Math.sin(i * 45.164) * 43758.5453; + const seed4 = Math.sin(i * 93.989) * 43758.5453; + const seed5 = Math.sin(i * 27.456) * 43758.5453; + + // Random horizontal position (0-100% of fill) + const sparklePosition = (seed1 - Math.floor(seed1)) * 100; + // Random vertical position (15-85% of height) + const topOffset = 15 + (seed2 - Math.floor(seed2)) * 70; + // Random size (0.5px to 2.5px) + const size = 0.5 + (seed3 - Math.floor(seed3)) * 2; + // Random animation duration (0.8s to 2.5s) + const animDuration = 0.8 + (seed4 - Math.floor(seed4)) * 1.7; + // Random animation delay (0s to 3s) + const animDelay = (seed5 - Math.floor(seed5)) * 3; + + return ( +
+ ); + })} + + )} +
+
+ + {/* 80% checkpoint marker */} + {showCheckpoint && ( +
+
+ 🎯 +
+
+ )} + + {/* Progress Icon (Rocket or custom) */} + {showRocket && ( +
+ + {progressIcon} + +
+ )} +
+ + {/* Fuel meter visual indicator - only show if not hidden */} + {!hidePercentage && ( +
+ 0 + + {Math.round(percentage)}% + + {requiredFuel} +
+ )} +
+ ); +} + diff --git a/src/lib/axl-explorations/src/components/GameCard.tsx b/src/lib/axl-explorations/src/components/GameCard.tsx new file mode 100644 index 00000000..c177fda4 --- /dev/null +++ b/src/lib/axl-explorations/src/components/GameCard.tsx @@ -0,0 +1,178 @@ +import { ReactNode } from "react"; +import { Card } from "./ui/card"; +import { Button } from "./ui/button"; +import { Progress } from "./ui/progress"; +import { cn } from "../lib/utils"; +import { TrendingUp, Clock, Target } from "lucide-react"; + +interface GameCardProps { + title: string; + description: string; + icon: ReactNode; + color: "primary" | "secondary" | "success" | "warning" | "purple" | "pink" | "teal"; + onClick: () => void; + className?: string; + difficulty?: "Easy" | "Medium" | "Hard"; + // Session tracking props - only show for combined games + progressPercentage?: number; + currentLevel?: number; + totalTimeSpent?: string; + completedLevels?: number; + totalLevels?: number; + successRate?: number; + totalSessions?: number; + isCombinedGame?: boolean; // New prop to control progress display +} + +const iconBackgroundColors = { + primary: "bg-blue-500", + secondary: "bg-blue-500", + success: "bg-blue-500", + warning: "bg-blue-500", + purple: "bg-blue-500", + pink: "bg-blue-500", + teal: "bg-blue-500" +}; + +const difficultyColors = { + Easy: "bg-green-500 text-white", + Medium: "bg-orange-500 text-white", + Hard: "bg-red-500 text-white" +}; + +const categoryColors = { + primary: "bg-orange-100 text-orange-600", + secondary: "bg-green-100 text-green-600", + success: "bg-green-100 text-green-600", + warning: "bg-green-100 text-green-600", + purple: "bg-purple-100 text-purple-600", + pink: "bg-purple-100 text-purple-600", + teal: "bg-orange-100 text-orange-600" +}; + +const categoryLabels = { + primary: "letter", + secondary: "word", + success: "word", + warning: "word", + purple: "sentence", + pink: "sentence", + teal: "letter" +}; + +export function GameCard({ + title, + description, + icon, + color, + onClick, + className, + difficulty = "Easy", + progressPercentage = 0, + currentLevel = 1, + totalTimeSpent = "0m", + completedLevels = 0, + totalLevels = 10, + successRate = 0, + totalSessions = 0, + isCombinedGame = false +}: GameCardProps) { + return ( + + {/* Category tag in top right */} +
+ + {categoryLabels[color]} + +
+ +
+ {/* Icon and title section */} +
+
+ {icon} +
+ +
+

+ {title} +

+
+ {difficulty} +
+
+
+ +

+ {description} +

+ + {/* Progress Information - Only show for combined games */} + {isCombinedGame && ( +
+ {/* Progress Bar */} +
+
+ + + Progress + + {progressPercentage}% +
+ +
+ {completedLevels} of {totalLevels} levels completed +
+
+ + {/* Current Level and Time Spent */} +
+
+ + Level {currentLevel} +
+
+ + {totalTimeSpent} +
+
+ + {/* Success Rate (only show if user has played) */} + {totalSessions > 0 && ( +
+ Success Rate: {successRate}% ({totalSessions} sessions) +
+ )} +
+ )} + + +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/GameCompleteScreen.tsx b/src/lib/axl-explorations/src/components/GameCompleteScreen.tsx new file mode 100644 index 00000000..32eb8a52 --- /dev/null +++ b/src/lib/axl-explorations/src/components/GameCompleteScreen.tsx @@ -0,0 +1,114 @@ +import { Button } from "./ui/button"; +import { Card } from "./ui/card"; +import { ArrowLeft } from "lucide-react"; + +interface StepResult { + step: number; + pairsFound: number; + movesUsed: number; + timeTaken: number; +} + +interface GameCompleteScreenProps { + stepResults: StepResult[]; + timeRemaining: number; + score: number; + onBack: () => void; +} + +export function GameCompleteScreen({ stepResults, timeRemaining, score, onBack }: GameCompleteScreenProps) { + // Format time helper + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + // Format time in minutes helper + const formatTimeInMinutes = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + if (mins === 0) { + return `${secs}s`; + } else if (secs === 0) { + return `${mins}m`; + } else { + return `${mins}m ${secs}s`; + } + }; + + const totalScore = score; + const totalTimeSpent = stepResults.reduce((sum, result) => sum + result.timeTaken, 0); + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Game Complete! +

+
+ +
+
+ + {/* Results Card */} + + {/* Congratulations Section */} +
+
+ 🏆 +
+

+ Congratulations! +

+

+ You did an amazing job! 🎉 +

+
+ + {/* Summary Stats */} +
+
+
{stepResults.reduce((sum, result) => sum + result.pairsFound, 0)}
+
Total Pairs
+
+
+
{stepResults.reduce((sum, result) => sum + result.movesUsed, 0)}
+
Total Moves
+
+
+
{formatTime(totalTimeSpent)}
+
Time Spent
+
+
+
{formatTime(timeRemaining)}
+
Time Remaining
+
+
+ + {/* Back Button */} +
+ +
+
+
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/GameIntroduction.tsx b/src/lib/axl-explorations/src/components/GameIntroduction.tsx new file mode 100644 index 00000000..e5c3fa39 --- /dev/null +++ b/src/lib/axl-explorations/src/components/GameIntroduction.tsx @@ -0,0 +1,153 @@ +import { useEffect, useState, useRef } from "react"; +import { Card } from "./ui/card"; +import { Button } from "./ui/button"; +import { Sparkles } from "lucide-react"; +import { useLanguage } from "../contexts/LanguageContext"; +import { Language } from "../constants/languages"; + +interface GameIntroductionProps { + title: string; + description?: string; + activityCount: number; + onContinue: () => void; + playNarration?: (text: string) => Promise; + customIcon?: React.ReactNode; + languageOverride?: Language; +} + +const introductionText = { + en: { + activities: "Activities", + continue: "Continue", + }, + te: { + activities: "కార్యకలాపాలు", + continue: "కొనసాగించండి", + }, + kn: { + activities: "ಚಟುವಟಿಕೆಗಳು", + continue: "ಮುಂದುವರಿಸಿ", + }, + mr: { + activities: "क्रियाकलाप", + continue: "सुरू ठेवा", + }, +}; + +export function GameIntroduction({ + title, + description, + activityCount, + onContinue, + playNarration, + customIcon, + languageOverride, +}: GameIntroductionProps) { + const { selectedLanguage } = useLanguage(); + const [showButton, setShowButton] = useState(false); + + const effectiveLanguage = languageOverride || selectedLanguage || 'en'; + const text = introductionText[effectiveLanguage]; + const lastNarrationKeyRef = useRef(null); + + useEffect(() => { + const narrationKey = `${effectiveLanguage}|${title}|${description}`; + if (lastNarrationKeyRef.current === narrationKey) { + return; + } + lastNarrationKeyRef.current = narrationKey; + + const playIntro = async () => { + const narrationText = `${title}. ${description}`; + + if (playNarration) { + await playNarration(narrationText); + } + + // Show button after narration with a small delay for effect + setTimeout(() => { + setShowButton(true); + }, 500); + }; + + playIntro(); + }, [title, description, effectiveLanguage, playNarration]); + + return ( + + +
+ {/* Animated Icon - Responsive sizing */} +
+
+ {customIcon || } +
+
+ + {/* Title */} + {title && ( +
+

+ {title} +

+
+ )} + + {/* Description */} + {description && ( +
+

+ {description} +

+
+ )} + + {/* Activity Count Display - Responsive and compact */} +
+
+ {/* Left sparkle */} +
+ +
+ + +
+
+ {activityCount} +
+
+ {text.activities} +
+
+ + + {/* Right sparkle */} +
+ +
+
+
+ + {/* Continue Button - Responsive and always visible */} + {showButton && ( +
+ + + {/* Pointer animation */} +
+ 👆 +
+
+ )} +
+
+ + ); +} + diff --git a/src/lib/axl-explorations/src/components/LanguageSelectionPopup.tsx b/src/lib/axl-explorations/src/components/LanguageSelectionPopup.tsx new file mode 100644 index 00000000..e6b816e1 --- /dev/null +++ b/src/lib/axl-explorations/src/components/LanguageSelectionPopup.tsx @@ -0,0 +1,95 @@ +import { useState } from "react"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"; +import { Globe, Check } from "lucide-react"; +import { Language, LANGUAGES, CONTENT_LANGUAGES, AUDIO_LANGUAGES, getLanguageByCode, LanguageOption } from "../constants/languages"; + +interface LanguageSelectionPopupProps { + selectedLanguage: Language; + onLanguageChange: (language: Language) => void; + trigger?: React.ReactNode; + title?: string; + description?: string; + icon?: React.ReactNode; + languages?: LanguageOption[]; // Optional: allows custom language list, defaults to CONTENT_LANGUAGES +} + +export function LanguageSelectionPopup({ + selectedLanguage, + onLanguageChange, + trigger, + title = 'Select Language', + description = 'Choose your preferred language for learning activities', + icon = , + languages = CONTENT_LANGUAGES // Default to content languages (excludes Hindi) +}: LanguageSelectionPopupProps) { + const [isOpen, setIsOpen] = useState(false); + + const handleLanguageSelect = (language: Language) => { + onLanguageChange(language); + setIsOpen(false); + }; + + const currentLanguage = getLanguageByCode(selectedLanguage); + + return ( + + + {trigger || ( + + )} + + + + + {icon} + {title} + + + {description} + + +
+ {languages.map((language) => ( + handleLanguageSelect(language.code)} + > + +
+
+ {language.flag} +
+ {language.name} + {language.nativeName} +
+
+ {selectedLanguage === language.code && ( +
+ +
+ )} +
+
+
+ ))} +
+
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/LetterLauncherLevelSelector.tsx b/src/lib/axl-explorations/src/components/LetterLauncherLevelSelector.tsx new file mode 100644 index 00000000..4d2e3d9b --- /dev/null +++ b/src/lib/axl-explorations/src/components/LetterLauncherLevelSelector.tsx @@ -0,0 +1,510 @@ +import { useState, useEffect } from 'react'; +import { Button } from './ui/button'; +import { ArrowLeft, Rocket, Fuel, Star, Lock } from 'lucide-react'; +import { SpaceBackground } from './SpaceBackground'; +import { DetectiveBackground } from './DetectiveBackground'; +import { SuperheroBackground } from './SuperheroBackground'; +import { getFuelRequirement, getMissionDestination } from '../utils/fuelCalculation'; +import { trackingAssessmentService } from '../utils/trackingAssessmentService'; +import { sessionManager } from '../utils/sessionManager'; +import { Language } from '../constants/languages'; +import { SPACE_LEVEL_CONFIG, DETECTIVE_LEVEL_CONFIG, SUPERHERO_LEVEL_CONFIG } from './levelConfigs/levelConfigs'; + +interface LevelStats { + level: number; + highestScore?: number; + totalMaxScore?: number; + lastAttemptedOn?: string; + isUnlocked?: boolean; + isCompleted?: boolean; + color?: string; + scorePercentage?: number; +} + +interface LetterLauncherLevelSelectorProps { + selectedLanguage: Language; + currentLevel: number; + maxLevels: number; + onLevelSelect: (level: number) => void; + onBack: () => void; + onDemo?: () => void; + gameKey: string; + // NEW optional props with defaults matching Letter Launcher behavior + theme?: 'space' | 'detective' | 'superhero'; + showStars?: boolean; + showFuel?: boolean; + title?: string; +} + + +// Level configurations are now imported from separate files in levelConfigs folder +// This allows for easier maintenance and adding new game themes in the future + +// Helper function to get star count based on fuel earned vs thresholds (for speed games) +// Uses same logic as success screen: midpoint between required and max fuel +const getStarCount = (fuelEarned: number, level: number): number => { + const { requiredFuel, maxFuel } = getFuelRequirement(level); + + // Same logic as Word Detective & Letter Launcher success screens + if (fuelEarned < requiredFuel) return 0; // Failed - no stars (or 1 if they passed but barely) + + const midpoint = requiredFuel + (maxFuel - requiredFuel) / 2; + if (fuelEarned > midpoint) return 3; + return 2; +}; + +export function LetterLauncherLevelSelector({ + selectedLanguage, + currentLevel, + maxLevels, + onLevelSelect, + onBack, + onDemo, + gameKey, + // NEW props with safe defaults (Letter Launcher behavior) + theme = 'space', + showStars = false, + showFuel = true, + title = 'Choose Your Mission' +}: LetterLauncherLevelSelectorProps) { + const [levelStats, setLevelStats] = useState>(new Map()); + const [isLoadingStats, setIsLoadingStats] = useState(false); + + useEffect(() => { + const fetchLevelStats = async () => { + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) return; + + setIsLoadingStats(true); + const gameName = gameKey.split('_')[0]; + const statsMap = new Map(); + + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + try { + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle object format (level1, level2, etc.) + if (result.success && result.data && typeof result.data === 'object' && !Array.isArray(result.data)) { + for (let level = 1; level <= maxLevels; level++) { + const levelKey = `level${level}`; + const levelData = (result.data as any)[levelKey]; + + // Process level if it exists in response (for speed games, all levels are returned) + if (levelData) { + const existingStats: LevelStats = { + level, + // Use backend's isUnlocked value directly (it handles speed game logic) + // Backend sets isUnlocked correctly for speed games (1-3 always unlocked) + isUnlocked: levelData.metadata?.isUnlocked ?? (level === 1), + isCompleted: levelData.metadata?.isCompleted || false + }; + + // Extract fuel from highest score (totalScore) or metadata + if (levelData.highest?.totalScore !== undefined && levelData.highest?.totalScore !== null) { + existingStats.highestScore = levelData.highest.totalScore; + } else if (levelData.metadata?.fuelEarned !== undefined) { + existingStats.highestScore = levelData.metadata.fuelEarned; + } else if (levelData.recent?.totalScore !== undefined) { + existingStats.highestScore = levelData.recent.totalScore; + } + + if (levelData.highest?.totalMaxScore !== undefined) { + existingStats.totalMaxScore = levelData.highest.totalMaxScore; + } else if (levelData.recent?.totalMaxScore !== undefined) { + existingStats.totalMaxScore = levelData.recent.totalMaxScore; + } + + if (levelData.metadata?.color) { + existingStats.color = levelData.metadata.color; + } + + // Extract lastAttemptedOn from highest or recent data + if (levelData.highest?.createdOn) { + existingStats.lastAttemptedOn = levelData.highest.createdOn; + } else if (levelData.recent?.createdOn) { + existingStats.lastAttemptedOn = levelData.recent.createdOn; + } + + // Calculate score percentage for stars display + if (existingStats.highestScore !== undefined && existingStats.totalMaxScore) { + existingStats.scorePercentage = (existingStats.highestScore / existingStats.totalMaxScore) * 100; + } else if (levelData.metadata?.scorePercentage !== undefined) { + existingStats.scorePercentage = levelData.metadata.scorePercentage; + } + + statsMap.set(level, existingStats); + } + } + } + // Handle array format (fallback for older API responses) + else if (result && result.data && Array.isArray(result.data)) { + result.data.forEach((item: any) => { + const level = item.level || 1; + const existingStats: LevelStats = statsMap.get(level) || { + level, + isUnlocked: true, + isCompleted: false + }; + + if (item.totalScore !== undefined && item.totalScore !== null) { + existingStats.highestScore = Math.max( + existingStats.highestScore || 0, + item.totalScore + ); + } + + if (item.totalMaxScore !== undefined && item.totalMaxScore !== null) { + existingStats.totalMaxScore = item.totalMaxScore; + } + + if (item.createdAt) { + const itemDate = new Date(item.createdAt); + if (!existingStats.lastAttemptedOn || itemDate > new Date(existingStats.lastAttemptedOn)) { + existingStats.lastAttemptedOn = item.createdAt; + } + } + + if (item.totalScore !== undefined && item.totalMaxScore !== undefined) { + const percentage = (item.totalScore / item.totalMaxScore) * 100; + if (percentage >= 80) { + existingStats.isCompleted = true; + } + } + + statsMap.set(level, existingStats); + }); + } + } catch (error) { + console.error('Error fetching level stats:', error); + } finally { + setIsLoadingStats(false); + setLevelStats(statsMap); + } + }; + + fetchLevelStats(); + }, [gameKey, selectedLanguage, maxLevels]); + + const handleLevelClick = (level: number) => { + // All levels are now unlocked - allow navigation to any level + onLevelSelect(level); + }; + + const getLevelFuelEarned = (level: number): number => { + const stats = levelStats.get(level); + return stats?.highestScore || 0; + }; + + const isLevelUnlocked = (level: number): boolean => { + // Use backend isUnlocked value if available, otherwise default to unlocked + const stats = levelStats.get(level); + // Default: level 1 always unlocked, others depend on backend + return stats?.isUnlocked ?? (level === 1); + }; + + // Get level card data based on theme (data-driven approach) + const getLevelCardData = (level: number) => { + const stats = levelStats.get(level); + const { requiredFuel, maxFuel } = getFuelRequirement(level); + const isCurrent = level === currentLevel; + const isCompleted = stats?.isCompleted || false; + + if (theme === 'detective') { + const detectiveConfig = DETECTIVE_LEVEL_CONFIG[level] || DETECTIVE_LEVEL_CONFIG[10]; + const levelFuelEarned = stats?.highestScore || 0; + const hasBeenPlayed = stats?.lastAttemptedOn || (stats?.highestScore !== undefined && stats.highestScore > 0); + + return { + icon: '🔍', + name: detectiveConfig.name, + scoreIconType: 'star', // 'star' for Star component, 'emoji' for string + scoreIcon: '⭐', + scoreEarned: hasBeenPlayed ? levelFuelEarned : 0, + scoreRequired: requiredFuel, + scoreColor: hasBeenPlayed + ? (levelFuelEarned >= requiredFuel ? 'text-green-400' : 'text-yellow-400') + : 'text-white/60', + image: detectiveConfig.image, + imageAlt: detectiveConfig.name, + imageWidth: 'clamp(140px, 20vw, 180px)', + imageHeight: 'clamp(140px, 20vh, 180px)', + imageLeft: '-20px', + glowColor: 'rgba(234, 179, 8, 0.15)', // Yellow + showScore: true, + showFuel: false + }; + } + + if (theme === 'superhero') { + const superheroConfig = SUPERHERO_LEVEL_CONFIG[level] || SUPERHERO_LEVEL_CONFIG[10]; + const levelFuelEarned = stats?.highestScore || 0; + const hasBeenPlayed = stats?.lastAttemptedOn || (stats?.highestScore !== undefined && stats.highestScore > 0); + + return { + icon: '🦸', + name: superheroConfig.name, + scoreIconType: 'star', // 'star' for Star component, 'emoji' for string + scoreIcon: '⭐', + scoreEarned: hasBeenPlayed ? levelFuelEarned : 0, + scoreRequired: requiredFuel, + scoreColor: hasBeenPlayed + ? (levelFuelEarned >= requiredFuel ? 'text-green-400' : 'text-yellow-400') + : 'text-white/60', + image: superheroConfig.image, + imageAlt: superheroConfig.name, + imageWidth: 'clamp(140px, 21vw, 190px)', + imageHeight: 'clamp(140px, 21vh, 190px)', + imageLeft: '0px', + glowColor: 'rgba(234, 179, 8, 0.15)', // Yellow + showScore: true, + showFuel: false + }; + } + + // Space theme (default) - Letter Launcher + const planetConfig = SPACE_LEVEL_CONFIG[level] || SPACE_LEVEL_CONFIG[10]; + const destination = getMissionDestination(level); + const fuelEarned = getLevelFuelEarned(level); + + return { + icon: '🚀', + name: destination, + scoreIconType: 'emoji', // 'star' for Star component, 'emoji' for string + scoreIcon: '⛽', + scoreEarned: fuelEarned, + scoreRequired: requiredFuel, + scoreColor: fuelEarned > 0 + ? (fuelEarned >= requiredFuel ? 'text-green-400' : 'text-yellow-400') + : 'text-white/60', + image: planetConfig.image, + imageAlt: destination, + imageWidth: 'clamp(180px, 25vw, 240px)', + imageHeight: 'clamp(200px, 28vh, 280px)', + imageLeft: '-30px', + glowColor: 'rgba(59, 130, 246, 0.2)', // Blue + showScore: showFuel, + showFuel: showFuel + }; + }; + + // Single reusable template for level card + const renderLevelCard = (level: number) => { + const cardData = getLevelCardData(level); + const isCurrent = level === currentLevel; + const stats = levelStats.get(level); + const { requiredFuel } = getFuelRequirement(level); + const fuelEarned = (theme === 'detective' || theme === 'superhero') + ? (stats?.highestScore || 0) + : getLevelFuelEarned(level); + // Level is completed if backend says so OR if score meets requirement + const isCompleted = stats?.isCompleted || (fuelEarned >= requiredFuel); + // Check if level is locked (use backend value) + const isLocked = !isLevelUnlocked(level); + + return ( +
!isLocked && handleLevelClick(level)} + className={` + relative w-full h-[120px] sm:h-[130px] + transition-all duration-300 overflow-hidden + ${isLocked + ? 'cursor-not-allowed opacity-60' + : 'cursor-pointer hover:scale-105 hover:shadow-2xl'} + ${isCurrent && !isLocked ? 'ring-4 ring-yellow-400 ring-opacity-75' : ''} + `} + style={{ + background: isLocked + ? 'rgba(0, 0, 0, 0.4)' + : isCompleted + ? 'rgba(255, 255, 255, 0.15)' + : 'transparent', + backdropFilter: (isCompleted || isLocked) ? 'blur(10px)' : 'none', + WebkitBackdropFilter: (isCompleted || isLocked) ? 'blur(10px)' : 'none', + borderRadius: '15px', + border: isLocked + ? '2px solid rgba(255, 255, 255, 0.1)' + : isCompleted + ? '2px solid rgba(255, 255, 255, 0.5)' + : '2px solid rgba(255, 255, 255, 0.2)', + boxShadow: isLocked + ? 'none' + : isCompleted + ? '0 8px 32px rgba(255, 255, 255, 0.15), inset 0 0 20px rgba(255, 255, 255, 0.1)' + : '0 8px 32px rgba(0, 0, 0, 0.2)' + }} + onMouseEnter={(e) => { + if (!isLocked) { + e.currentTarget.style.border = '2px solid white'; + } + }} + onMouseLeave={(e) => { + if (!isLocked) { + e.currentTarget.style.border = isCompleted + ? '2px solid rgba(255, 255, 255, 0.5)' + : '2px solid rgba(255, 255, 255, 0.2)'; + } + }} + > + {/* Content - Horizontal Layout: Text Left, Image Right */} +
+ {/* Left Section - Text Content */} +
+
+ {/* Icon + Name */} +
+ {cardData.icon} + {cardData.name} +
+ + {/* Score/Fuel Display */} + {cardData.showScore && ( +
+ {cardData.scoreIconType === 'star' ? ( + + ) : ( + {cardData.scoreIcon} + )} + + {isLoadingStats && theme === 'space' ? ( + Loading... + ) : ( + <> + {cardData.scoreEarned > 0 ? ( + + {cardData.scoreEarned} + + ) : ( + 0 + )} + / {cardData.scoreRequired} + + )} + +
+ )} +
+
+
+ + {/* Right Section - Level Image */} +
+ {cardData.imageAlt} +
+ + {/* Glow Effect - only show for unlocked levels */} + {!isLocked && ( +
+ )} + + {/* Lock Overlay for locked levels */} + {isLocked && ( +
+
+ +
+
+ )} +
+ ); + }; + + // Common content (header + level grid) + const renderContent = () => ( + <> + {/* Header */} +
+
+ + {onDemo && ( + + )} +
+

+ {title} +

+
+ + {/* Level Boxes Container */} +
+
+ {Array.from({ length: maxLevels }, (_, i) => renderLevelCard(i + 1))} +
+
+ + ); + + // Render with appropriate background based on theme + if (theme === 'detective') { + return ( + + {/* Extra dark overlay for level selector page only */} +
+
+ {renderContent()} +
+ + ); + } + + if (theme === 'superhero') { + return ( + + {/* Extra dark overlay for level selector page only */} +
+
+ {renderContent()} +
+ + ); + } + + return ( + + {renderContent()} + + ); +} + diff --git a/src/lib/axl-explorations/src/components/LevelSelector.tsx b/src/lib/axl-explorations/src/components/LevelSelector.tsx new file mode 100644 index 00000000..a4b9af75 --- /dev/null +++ b/src/lib/axl-explorations/src/components/LevelSelector.tsx @@ -0,0 +1,376 @@ +import { Button } from "./ui/button"; +import { Card } from "./ui/card"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip"; +import { ArrowLeft, TrendingUp, Award,PlayCircle, Lock as LockIcon, Star } from "lucide-react"; +import { Language, LANGUAGES } from "../constants/languages"; +import { useState, useEffect } from "react"; +import { trackingAssessmentService } from "../utils/trackingAssessmentService"; +import { sessionManager } from "../utils/sessionManager"; + +interface LevelStats { + level: number; + highestScore?: number; + totalMaxScore?: number; + lastAttemptedOn?: string; + scorePercentage?: number; + isUnlocked?: boolean; + isCompleted?: boolean; + color?: string; +} + +interface LevelSelectorProps { + selectedLanguage: Language; + currentLevel: number; + maxLevels: number; + onLevelSelect: (level: number) => void; + onBack: () => void; + onDemo?: () => void; // Add demo callback + gameTitle?: string; + showBadge?: boolean; + onCollectBadge?: () => void; + badgeTooltip?: string; // Add custom tooltip text + gameKey?: string; // Add gameKey to fetch level stats + unlockAll?: boolean; // When true, ignore backend locks and unlock all levels +} + +export function LevelSelector({ + selectedLanguage, + currentLevel, + maxLevels, + onLevelSelect, + onBack, + onDemo, + gameTitle, + showBadge = false, + onCollectBadge, + badgeTooltip = "Coming Soon", + gameKey, + unlockAll +}: LevelSelectorProps) { + const language = LANGUAGES.find(l => l.code === selectedLanguage); + const [levelStats, setLevelStats] = useState>(new Map()); + const [isLoadingStats, setIsLoadingStats] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + + // Fetch level stats on mount + useEffect(() => { + const fetchLevelStats = async () => { + if (!gameKey) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) return; + + setIsLoadingStats(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + const statsMap = new Map(); + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage // Use language as unitId + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Process each level from backend response + Object.keys(result.data).forEach(levelKey => { + if (!levelKey.startsWith('level')) return; + + const levelData = result.data[levelKey]; + const levelNumber = parseInt(levelKey.replace('level', '')); + + // Process level data even if metadata structure varies + if (levelData) { + // Extract percentage from metadata if available, otherwise calculate from highest score + const metadata = levelData.metadata || {}; + const highest = levelData.highest || {}; + const scorePercentage = metadata.scorePercentage ?? + (highest.totalMaxScore > 0 ? ((highest.totalScore || 0) / highest.totalMaxScore * 100) : 0); + + statsMap.set(levelNumber, { + level: levelNumber, + highestScore: highest.totalScore, + totalMaxScore: highest.totalMaxScore || 10, + lastAttemptedOn: highest.createdOn, + scorePercentage: scorePercentage, + isUnlocked: metadata.isUnlocked ?? (levelNumber === 1 || highest.totalScore > 0), + isCompleted: metadata.isCompleted ?? false, + color: metadata.color + }); + } + }); + + // For combined games, use backend's currentLevel directly (calculated with 80% validation) + // For individual games, compute from progress + const isCombinedGame = gameName && ( + gameName.startsWith('combinedLetter') || + gameName.startsWith('combinedWord') || + gameName.startsWith('combinedSentence') + ); + + if (isCombinedGame) { + // Combined games: Use backend's currentLevel directly (trust backend calculation) + const backendProvided = result.metadata?.currentLevel || 1; + setBackendCurrentLevel(Math.min(Math.max(1, backendProvided), maxLevels)); + } else { + // Individual games: Compute current level based on successful progress + let highestSuccessfulLevel = 0; + statsMap.forEach((value, levelNum) => { + const percent = value.scorePercentage ?? 0; + const completed = value.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNum); + } + }); + const computedFromProgress = Math.min( + Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), + maxLevels + ); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min( + Math.max(computedFromProgress, backendProvided), + maxLevels + ); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } + setLevelStats(statsMap); + setIsLoadingStats(false); + }; + + fetchLevelStats(); + }, [gameKey, selectedLanguage, maxLevels]); + + // Use English text for all languages (including Telugu and Marathi) + const getLocalizedText = () => { + return { + title: 'Choose Level', + subtitle: 'All levels are unlocked for practice', + currentLevel: `Current Level: ${backendCurrentLevel}/${maxLevels}`, + backButton: 'Back', + complete: 'Complete', + current: 'Current', + available: 'Available', + locked: 'Locked', + allLevelsMessage: '🎮 All levels are available to play!', + practiceMessage: '💡 Practice any level to strengthen your skills' + }; + }; + + const localizedText = getLocalizedText(); + const levels = Array.from({ length: maxLevels }, (_, i) => i + 1); + const isLetterBadge = !!gameKey && gameKey.startsWith('combinedLetter'); + const isBadgeUnlocked = isLetterBadge && !!(levelStats.get(maxLevels)?.isCompleted); + const effectiveBadgeTooltip = isLetterBadge + ? (isBadgeUnlocked ? badgeTooltip : `Complete Level ${maxLevels} to unlock`) + : (badgeTooltip || 'Coming Soon'); + + // Helper function to get star count based on percentage + const getStarCount = (percentage: number): number => { + if (percentage >= 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + return 0; // Less than 80% - show greyed out stars + }; + + return ( +
+
+ {/* Header */} +
+ + +
+

+ {gameTitle} +

+
+ + {/* Demo Button */} + {onDemo && ( + + )} + {!onDemo &&
} +
+ + {/* Level Selection */} + +
+ +

+ {localizedText.title} +

+ {/*

+ {localizedText.subtitle} +

+

+ {localizedText.currentLevel} +

*/} +
+ + +
+ {levels.map((level) => { + // For unlockAll (individual games), no current highlight + const isCurrent = !unlockAll && level === backendCurrentLevel; + const stats = levelStats.get(level); + + // Use backend-provided unlock/completion status + const isUnlocked = unlockAll ? true : (stats?.isUnlocked ?? (level === 1)); + const isCompleted = stats?.isCompleted ?? false; + const isLocked = !isUnlocked; + const currentPercent = stats?.scorePercentage ?? 0; + + // Get color from metadata, but only use it if it's not "white" + const levelColor = stats?.color; + const useCustomColor = levelColor && levelColor.toLowerCase() !== 'white'; + + // Determine border color style with thicker stroke + const borderStyle: { borderColor?: string; borderWidth?: string } = {}; + if (!isLocked && useCustomColor) { + borderStyle.borderColor = levelColor; + borderStyle.borderWidth = '3px'; // Thicker stroke for custom colors + } + + const variantClasses = isLocked + ? 'bg-gray-100 text-gray-400 border-gray-300 cursor-not-allowed hover:scale-100' + : (isCurrent + ? useCustomColor + ? 'bg-white text-black hover:bg-white hover:text-black' + : 'bg-white text-black border-blue-500 hover:bg-white hover:text-black hover:border-blue-600' + : useCustomColor + ? 'bg-white hover:bg-blue-50' + : 'bg-white hover:bg-blue-50 hover:border-blue-300'); + + return ( + + + + + {isLocked && ( + +

Score 80%+ in Level {level - 1} to unlock

+
+ )} +
+ ); + })} + + {/* Add badge level if showBadge is true */} + {showBadge && ( + + +
+ +
+
+ +

{effectiveBadgeTooltip}

+
+
+ )} +
+
+ + {/*
+

{localizedText.allLevelsMessage}

+

{localizedText.practiceMessage}

+
*/} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/Lives.tsx b/src/lib/axl-explorations/src/components/Lives.tsx new file mode 100644 index 00000000..dd47e9bd --- /dev/null +++ b/src/lib/axl-explorations/src/components/Lives.tsx @@ -0,0 +1,88 @@ +import { Heart } from "lucide-react"; +import { cn } from "../lib/utils"; +import { useState, useEffect } from "react"; + +interface LivesProps { + lives: number; + maxLives?: number; + className?: string; + size?: "sm" | "md" | "lg"; + onLifeLost?: () => void; // Callback when a life is lost +} + +export function Lives({ + lives, + maxLives = 3, + className = "", + size = "lg", + onLifeLost +}: LivesProps) { + const [previousLives, setPreviousLives] = useState(lives); + const [justLostLife, setJustLostLife] = useState(null); + + const sizeClasses = { + sm: "h-4 w-4 sm:h-5 sm:w-5 md:h-6 md:w-6", + md: "h-5 w-5 sm:h-6 sm:w-6 md:h-7 md:w-7 lg:h-8 lg:w-8", + lg: "h-6 w-6 sm:h-7 sm:w-7 md:h-8 md:w-8 lg:h-9 lg:w-9 xl:h-10 xl:w-10" + }; + + // Detect when a life is lost + useEffect(() => { + if (lives < previousLives) { + // Calculate which life was lost + const lostLifeNumber = maxLives - lives; // The lost life corresponds to this number + setJustLostLife(lostLifeNumber); + + // Call callback if provided + if (onLifeLost) { + onLifeLost(); + } + + // Clear the animation after it completes + const timer = setTimeout(() => { + setJustLostLife(null); + }, 800); + + return () => clearTimeout(timer); + } + setPreviousLives(lives); + }, [lives, previousLives, onLifeLost]); + + return ( +
+ {Array.from({ length: maxLives }, (_, index) => { + const lifeNumber = index + 1; + // Lives are removed from left to right + const isAlive = index >= (maxLives - lives); + const isJustLost = justLostLife === lifeNumber; + + return ( +
+ + {/* Sparkle effect when life is lost */} + {isJustLost && ( +
+
+ ✨ +
+
+ )} +
+ ); + })} +
+ ); +} diff --git a/src/lib/axl-explorations/src/components/LoginDashboard.tsx b/src/lib/axl-explorations/src/components/LoginDashboard.tsx new file mode 100644 index 00000000..8ee3ba0a --- /dev/null +++ b/src/lib/axl-explorations/src/components/LoginDashboard.tsx @@ -0,0 +1,227 @@ +import { useState, useEffect } from 'react'; +import { Button } from "./ui/button"; +import { Input } from "./ui/input"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "./ui/alert-dialog"; +import { Badge } from "./ui/badge"; +import { User, LogIn, Trash2, UserPlus, Clock, Calendar } from 'lucide-react'; +import { sessionManager, User as UserType } from "../utils/sessionManager"; +import { sessionTelemetryManager } from "../utils/sessionTelemetryManager"; + +interface LoginDashboardProps { + onLogin: (username: string) => void; +} + +export const LoginDashboard = ({ onLogin }: LoginDashboardProps) => { + const [username, setUsername] = useState(''); + const [users, setUsers] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + // Load existing users + setUsers(sessionManager.getUsers()); + }, []); + + const handleLogin = async (loginUsername?: string) => { + const userToLogin = loginUsername || username.trim(); + if (!userToLogin) return; + + setIsLoading(true); + + // Simulate a brief loading state + await new Promise(resolve => setTimeout(resolve, 500)); + + const success = sessionManager.login(userToLogin); + if (success) { + // Start telemetry session + // await sessionTelemetryManager.startUserSession(userToLogin); + onLogin(userToLogin); + } + + setIsLoading(false); + }; + + const handleQuickLogin = (user: UserType) => { + handleLogin(user.username); + }; + + const handleDeleteUser = (username: string) => { + const success = sessionManager.deleteUser(username); + if (success) { + setUsers(sessionManager.getUsers()); + } + }; + + const formatDate = (dateString: string) => { + const date = new Date(dateString); + const now = new Date(); + const diffInHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60)); + + if (diffInHours < 1) { + return 'Just now'; + } else if (diffInHours < 24) { + return `${diffInHours}h ago`; + } else { + const diffInDays = Math.floor(diffInHours / 24); + return `${diffInDays}d ago`; + } + }; + + return ( +
+
+ {/* Header */} +
+
+ +
+

+ Welcome to AXL Learning +

+

+ Choose your profile to start your learning journey +

+
+ +
0 ? 'grid-cols-1 md:grid-cols-2' : 'grid-cols-1 max-w-md mx-auto'}`}> + {/* New User Login */} + + + + + {users.length > 0 ? 'New User Login' : 'Get Started'} + + + {users.length > 0 + ? 'Enter your username to create a new profile or login' + : 'Enter your username to create your profile and start learning' + } + + + +
+ + setUsername(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && handleLogin()} + className="h-10 sm:h-12 text-base sm:text-lg" + /> +
+ +
+
+ + {/* Existing Users - Only show when there are users */} + {users.length > 0 && ( + + + + + Recent Profiles + + + Select from your recent profiles to continue learning + + + +
+ {users.map((user, index) => ( +
+
+
+ + {user.username.charAt(0).toUpperCase()} + +
+
+

+ {user.username} +

+
+
+ + {new Date(user.loginTime).toLocaleDateString()} +
+
+
+
+
+ + + + + + + + Delete Profile + + Are you sure you want to delete the profile "{user.username}"? + This action cannot be undone and will remove all associated data. + + + + Cancel + handleDeleteUser(user.username)} + className="bg-red-500 hover:bg-red-600" + > + Delete + + + + +
+
+ ))} +
+
+
+ )} +
+ +
+
+ ); +}; diff --git a/src/lib/axl-explorations/src/components/Mascot.tsx b/src/lib/axl-explorations/src/components/Mascot.tsx new file mode 100644 index 00000000..d96cb6df --- /dev/null +++ b/src/lib/axl-explorations/src/components/Mascot.tsx @@ -0,0 +1,50 @@ +import { ReactNode } from "react"; +import { cn } from "../lib/utils"; + +interface MascotProps { + className?: string; + message?: string; + mood?: "happy" | "excited" | "encouraging" | "proud"; +} + +const moodMessages = { + happy: "Ready for some fun learning? 🎉", + excited: "Wow! You're doing amazing! ⭐", + encouraging: "Keep going! You've got this! 💪", + proud: "I'm so proud of your progress! 🏆" +}; + +export function Mascot({ className, message, mood = "happy" }: MascotProps) { + const displayMessage = message || moodMessages[mood]; + + return ( +
+ {/* Speech bubble */} +
+

+ {displayMessage} +

+ {/* Arrow pointing down */} +
+
+
+
+ + {/* Mascot Character */} +
+ {/* Simple owl face */} +
+ 🦉 +
+ + {/* Sparkle effects */} +
+ ✨ +
+
+ ⭐ +
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/Navbar.tsx b/src/lib/axl-explorations/src/components/Navbar.tsx new file mode 100644 index 00000000..377f8e8c --- /dev/null +++ b/src/lib/axl-explorations/src/components/Navbar.tsx @@ -0,0 +1,124 @@ +import { useNavigate } from "react-router-dom"; +import { Button } from "./ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + DropdownMenuSeparator, +} from "./ui/dropdown-menu"; +import { + Globe, + ChevronDown, + LogOut, + UserCircle, + Grid3X3, + Volume2 +} from "lucide-react"; +import { LanguageSelectionPopup } from "./LanguageSelectionPopup"; +import { useLanguage } from "../contexts/LanguageContext"; +import { useAudioLanguage } from "../contexts/AudioLanguageContext"; +import { getLanguageByCode, AUDIO_LANGUAGES } from "../constants/languages"; + +interface NavbarProps { + currentUser: string; + onLogout: () => void; + showViewAllButton?: boolean; +} + +const Navbar = ({ + currentUser, + onLogout, + showViewAllButton = true +}: NavbarProps) => { + const navigate = useNavigate(); + const { selectedLanguage, setSelectedLanguage } = useLanguage(); + const { selectedAudioLanguage, setSelectedAudioLanguage } = useAudioLanguage(); + const audioLanguageDetails = getLanguageByCode(selectedAudioLanguage); + + return ( +
+
+ + {/* Right side - User profile, View All button and Language Settings */} +
+ + + {showViewAllButton && ( + + )} + + {/* User Profile Dropdown */} + + + + + +
+

Logged in as

+

{currentUser}

+
+ } + languages={AUDIO_LANGUAGES} + trigger={ + event.preventDefault()} + > +
+ {audioLanguageDetails?.flag || '🎧'} +
+
+ Audio instructions + + {audioLanguageDetails?.name || 'English'} + +
+
+ } + /> + + + + Logout + +
+
+
+
+
+ ); +}; + +export default Navbar; diff --git a/src/lib/axl-explorations/src/components/PlanetWithRocketAnimation.tsx b/src/lib/axl-explorations/src/components/PlanetWithRocketAnimation.tsx new file mode 100644 index 00000000..4e7b8df7 --- /dev/null +++ b/src/lib/axl-explorations/src/components/PlanetWithRocketAnimation.tsx @@ -0,0 +1,130 @@ +import { PlanetIcon } from './ui/PlanetIcon'; + +interface PlanetWithRocketAnimationProps { + level: number; + planetSize?: string; // e.g., "text-6xl sm:text-7xl md:text-8xl" + containerSize?: { + width: string; + height: string; + }; + orbitSize?: { + width: string; + height: string; + }; + rocketSize?: { + width: string; + height: string; + }; + showOrbitPath?: boolean; + className?: string; +} + +export function PlanetWithRocketAnimation({ + level, + planetSize = "text-6xl sm:text-7xl md:text-8xl", + containerSize = { + width: 'clamp(120px, 30vw, 200px)', + height: 'clamp(120px, 30vw, 200px)' + }, + orbitSize = { + width: 'clamp(100px, 25vw, 180px)', + height: 'clamp(100px, 25vw, 180px)' + }, + rocketSize = { + width: 'clamp(20px, 5vw, 32px)', + height: 'clamp(28px, 7vw, 44px)' + }, + showOrbitPath = true, + className = "" +}: PlanetWithRocketAnimationProps) { + return ( + <> + +
+ + + {/* Orbit path (optional visual) */} + {showOrbitPath && ( +
+ )} + + {/* Orbiting rocket container */} +
+ {/* Rocket positioned at edge, pointing tangent to orbit */} +
+ + {/* Rocket body */} + + + {/* Nose cone */} + + {/* Window */} + + + {/* Fins */} + + + {/* Flame */} + + + + + + +
+
+
+ + ); +} + diff --git a/src/lib/axl-explorations/src/components/ProgressBar.tsx b/src/lib/axl-explorations/src/components/ProgressBar.tsx new file mode 100644 index 00000000..dc31267e --- /dev/null +++ b/src/lib/axl-explorations/src/components/ProgressBar.tsx @@ -0,0 +1,129 @@ +import { Progress } from "./ui/progress"; +import { Trophy, Star, Heart } from "lucide-react"; +import { cn } from "../lib/utils"; +import { useState, useEffect } from "react"; + +interface ProgressBarProps { + current: number; + total: number; + score?: number; + className?: string; + showCompleteMessage?: boolean; + lives?: number; + maxLives?: number; + onLifeLost?: () => void; +} + +export function ProgressBar({ + current, + total, + score, + className, + showCompleteMessage = true, + lives, + maxLives = 3, + onLifeLost +}: ProgressBarProps) { + const percentage = (current / total) * 100; + const isComplete = current === total; + const [previousLives, setPreviousLives] = useState(lives ?? maxLives); + const [justLostLife, setJustLostLife] = useState(null); + + // Size classes matching Lives component "lg" size + const iconSizeClasses = "h-6 w-6 sm:h-7 sm:w-7 md:h-8 md:w-8 lg:h-9 lg:w-9 xl:h-10 xl:w-10"; + + // Detect when a life is lost + useEffect(() => { + if (lives !== undefined && lives < previousLives) { + const lostLifeNumber = maxLives - lives; + setJustLostLife(lostLifeNumber); + + if (onLifeLost) { + onLifeLost(); + } + + const timer = setTimeout(() => { + setJustLostLife(null); + }, 800); + + return () => clearTimeout(timer); + } + if (lives !== undefined) { + setPreviousLives(lives); + } + }, [lives, previousLives, maxLives, onLifeLost]); + + return ( +
+
+ + Progress: {current}/{total} + +
+ + + + {/* Stars and Lives on the same line - Stars on left, Lives on right */} + {(score !== undefined || lives !== undefined) && ( +
+ {/* Star score - Left side */} + {score !== undefined && ( +
+ + {/* {score} */} + {score} +
+ )} + + {/* Lives Display - Right side, aligned with star */} + {lives !== undefined && ( +
+ {Array.from({ length: maxLives }, (_, index) => { + const lifeNumber = index + 1; + const isAlive = index >= (maxLives - lives); + const isJustLost = justLostLife === lifeNumber; + + return ( +
+ + {/* Sparkle effect when life is lost */} + {isJustLost && ( +
+
+ ✨ +
+
+ )} +
+ ); + })} +
+ )} +
+ )} + + {/* {isComplete && showCompleteMessage && ( +
+ + Level Complete! +
+ )} */} +
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/SpaceBackground.tsx b/src/lib/axl-explorations/src/components/SpaceBackground.tsx new file mode 100644 index 00000000..1cefd026 --- /dev/null +++ b/src/lib/axl-explorations/src/components/SpaceBackground.tsx @@ -0,0 +1,507 @@ +import React from 'react'; + +interface SpaceBackgroundProps { + children: React.ReactNode; + className?: string; +} + +export function SpaceBackground({ children, className = '' }: SpaceBackgroundProps) { + return ( + <> + +
+ {/* Aurora effect at edges */} +
+
+
+
+ + {/* Deep space layer - furthest (tiny dim stars) */} +
+ {[...Array(50)].map((_, i) => ( +
+ ))} +
+ + {/* Galaxy Nebula Effects - mid depth */} +
+ {/* Purple nebula cloud 1 */} +
+ {/* Blue nebula cloud 2 */} +
+ {/* Pink nebula cloud 3 */} +
+ {/* Cyan accent nebula */} +
+
+ + {/* Mid-layer stars - colored variety */} +
+ {[...Array(35)].map((_, i) => ( +
+ ))} +
+ + {/* Foreground stars - brightest, closest */} +
+ {[...Array(12)].map((_, i) => ( +
+ ))} + {/* Distant galaxy spiral */} +
+
+ + {/* Cosmic dust particles */} +
+ {[...Array(15)].map((_, i) => ( +
+ ))} +
+ + {/* Shooting stars with glowing trails */} +
+ {[...Array(3)].map((_, i) => ( +
+ ))} +
+ + {/* Light rays from corners */} +
+
+
+
+
+ + {/* Floating light orbs */} +
+ {[...Array(5)].map((_, i) => ( +
+ ))} +
+ + {/* Sparkle bursts */} +
+ {[...Array(6)].map((_, i) => ( +
+ + + +
+ ))} +
+ + {/* Comet with long tail */} +
+
+
+
+
+
+ + {/* Constellation pattern (subtle) */} +
+ + + + + + + + + + + + +
+ + {/* Tiny distant galaxies */} +
+ {/* Spiral galaxy 1 */} +
+ + + + + + + + + + +
+ {/* Spiral galaxy 2 */} +
+ + + + + + + + + + +
+
+ + {/* Content */} +
+ {children} +
+
+ + ); +} + diff --git a/src/lib/axl-explorations/src/components/SuccessScreen.tsx b/src/lib/axl-explorations/src/components/SuccessScreen.tsx new file mode 100644 index 00000000..9668cdfb --- /dev/null +++ b/src/lib/axl-explorations/src/components/SuccessScreen.tsx @@ -0,0 +1,209 @@ +import { useState, useEffect } from "react"; +import { Button } from "./ui/button"; +import { Card } from "./ui/card"; +import { + Trophy, + Star, + Sparkles, + Crown, + Medal, + Gift, + ArrowRight, + RotateCcw +} from "lucide-react"; +import { cn } from "../lib/utils"; + +interface SuccessScreenProps { + gameTitle: string; + score: number; + totalQuestions: number; + totalAttempts?: number; + starsEarned: number; + newAchievements?: string[]; + onPlayAgain: () => void; + onBackToHub: () => void; + onNextLevel?: () => void; + hasNextLevel?: boolean; + continueButtonText?: string; // Optional: custom text for the continue/next level button +} + +export function SuccessScreen({ + gameTitle, + score, + totalQuestions, + totalAttempts, + starsEarned, + newAchievements = [], + onPlayAgain, + onBackToHub, + onNextLevel, + hasNextLevel = false, + continueButtonText +}: SuccessScreenProps) { + const [showConfetti, setShowConfetti] = useState(true); + const [currentStep, setCurrentStep] = useState(0); + + const attemptsUsed = Math.max(totalAttempts ?? totalQuestions, 1); + const perfectScore = score === attemptsUsed; + const percentage = Math.round((score / attemptsUsed) * 100); + + // Animation sequence + useEffect(() => { + const timers = [ + setTimeout(() => setCurrentStep(1), 500), + setTimeout(() => setCurrentStep(2), 1200), + setTimeout(() => setCurrentStep(3), 2000), + setTimeout(() => setShowConfetti(false), 8000) + ]; + + return () => timers.forEach(timer => clearTimeout(timer)); + }, []); + + const confettiElements = Array.from({ length: 50 }, (_, i) => ({ + id: i, + emoji: ['🎉', '⭐', '🌟', '✨', '🎊', '🏆', '👏'][Math.floor(Math.random() * 7)], + delay: Math.random() * 3, + duration: 3 + Math.random() * 2, + left: Math.random() * 100, + })); + + return ( +
+ {/* Confetti Animation */} + {showConfetti && ( +
+ {confettiElements.map((confetti) => ( +
+ {confetti.emoji} +
+ ))} +
+ )} + + {/* Floating background elements */} +
+
☁️
+
🌈
+
+
+
+ +
+ {/* Main Success Card */} + + {/* Trophy and Title */} +
= 0 ? "opacity-100 scale-100" : "opacity-0 scale-50" + )}> +
+
+ {perfectScore ? ( + + ) : ( + + )} +
+ + {/* Floating stars around trophy */} +
+
+
🌟
+
+
+ +
+ {percentage === 100 && "Amazing! You got everything right!"} + {percentage >= 90 && percentage < 100 && "🎯 Excellent work! You're almost perfect!"} + {percentage >= 80 && percentage < 90 && "👏 Great job! You're learning so well!"} + {percentage >= 70 && percentage < 80 && "💪 Good effort! Keep practicing!"} + {percentage < 70 && "🌱 Nice try! Every attempt makes you stronger!"} +
+ {/* Removed game title completion line as requested */} +
+ + {/* Stars Display */} +
= 1 ? "opacity-100 scale-100" : "opacity-0 scale-50" + )}> +
+ {/*

Stars Earned!

*/} +
+ {[...Array(3)].map((_, i) => ( + + ))} +
+
+
+ + + {/* Action Buttons */} +
= 2 ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8" + )}> +
+ {hasNextLevel && ( + + )} + + {!hasNextLevel && ( + + )} +
+
+ + {/* Fun mascot message */} +
= 3 ? "opacity-100 scale-100" : "opacity-0 scale-50" + )}> +
+
🦉
+
+
+ "I'm so proud of you! Keep up the amazing work!" +
+
+
+ + {/* Removed perfect score bonus banner */} +
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/SuperheroBackground.tsx b/src/lib/axl-explorations/src/components/SuperheroBackground.tsx new file mode 100644 index 00000000..4d5b2f3d --- /dev/null +++ b/src/lib/axl-explorations/src/components/SuperheroBackground.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +interface SuperheroBackgroundProps { + children: React.ReactNode; + className?: string; +} + +export function SuperheroBackground({ children, className = '' }: SuperheroBackgroundProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/lib/axl-explorations/src/components/TryAgain.tsx b/src/lib/axl-explorations/src/components/TryAgain.tsx new file mode 100644 index 00000000..64bbe87b --- /dev/null +++ b/src/lib/axl-explorations/src/components/TryAgain.tsx @@ -0,0 +1,207 @@ +import { useState, useEffect } from "react"; +import { Card } from "./ui/card"; +import { Button } from "./ui/button"; +import { TrendingUp, RotateCcw, ChevronRight, Rocket, Fuel } from "lucide-react"; +import { Language } from "../constants/languages"; +import { SpaceBackground } from "./SpaceBackground"; + +interface TryAgainProps { + totalCorrect: number; + totalQuestions: number; + selectedLanguage: Language; + currentLevel: number; + gameKey: string; + onTryAgain: () => void; + onBackToHome: () => void; + livesLost?: boolean; // Flag to indicate if game ended due to lives lost + fuelMode?: boolean; // Show fuel-based completion screen + fuelCollected?: number; // Fuel collected + fuelRequired?: number; // Fuel required to pass + destination?: string; // Mission destination name + useSpaceBackground?: boolean; // Use space background +} + +export function TryAgain({ + totalCorrect, + totalQuestions, + selectedLanguage, + currentLevel, + gameKey, + onTryAgain, + onBackToHome, + livesLost = false, + fuelMode = false, + fuelCollected = 0, + fuelRequired = 0, + destination = "", + useSpaceBackground = false +}: TryAgainProps) { + const [isVisible, setIsVisible] = useState(false); + const scorePercentage = totalQuestions > 0 ? (totalCorrect / totalQuestions) * 100 : 0; + + // Add CSS for twinkle animation if not already added + useEffect(() => { + if (!document.getElementById('try-again-twinkle-style')) { + const style = document.createElement('style'); + style.id = 'try-again-twinkle-style'; + style.textContent = ` + @keyframes twinkle { + 0%, 100% { opacity: 0.3; transform: scale(1); } + 50% { opacity: 1; transform: scale(1.2); } + } + `; + document.head.appendChild(style); + } + }, []); + + // Add delay before showing the component for smooth transition + useEffect(() => { + const timer = setTimeout(() => { + setIsVisible(true); + }, 200); // 200ms delay before showing + + return () => clearTimeout(timer); + }, []); + + const content = ( +
+ + {/* Starry background effect for fuel mode */} + {fuelMode && useSpaceBackground && ( +
+ {[...Array(30)].map((_, i) => ( +
+ ))} +
+ )} +
+
+ {/* Rocket icon for fuel mode */} + {fuelMode && ( +
+ +
+ )} + +
+ {livesLost ? + <>💔 + 💔 + 💔 + : + fuelMode ? null : + } +
+ + {/* Fuel mode display */} + {fuelMode && ( + <> +

+ Need more fuel! +

+
+ + + {fuelCollected} / {fuelRequired} + + +
+
+ ⏱️ Too slow. Tap faster to fill the 🚀 fuel +
+ + )} + + {/* "Game Over!" text - only in English, for all languages */} + {livesLost && ( +

+ Game Over! +

+ )} + { !livesLost && !fuelMode && <> +

+ {selectedLanguage === 'te' ? 'మంచి ప్రయత్నం! 💪' : + selectedLanguage === 'mr' ? 'चांगला प्रयत्न! 💪' : + selectedLanguage === 'kn' ? 'ಒಳ್ಳೆಯ ಪ್ರಯತ್ನ! 💪' : + 'Good Try! 💪'} +

+

+ {selectedLanguage === 'te' ? + `మీరు ${totalCorrect} / ${totalQuestions} ప్రశ్నలకు సరైన సమాధానాలు ఇచ్చారు (${scorePercentage.toFixed(0)}%)` : + selectedLanguage === 'mr' ? + `तुम्ही ${totalCorrect} / ${totalQuestions} प्रश्नांना योग्य उत्तरे दिली (${scorePercentage.toFixed(0)}%)` : + selectedLanguage === 'kn' ? + `ನೀವು ${totalCorrect} / ${totalQuestions} ಪ್ರಶ್ನೆಗಳಿಗೆ ಸರಿಯಾಗಿ ಉತ್ತರಿಸಿದ್ದೀರಿ (${scorePercentage.toFixed(0)}%)` : + `You got ${totalCorrect} / ${totalQuestions} questions correct (${scorePercentage.toFixed(0)}%)` + } +

+

+ {selectedLanguage === 'te' ? + 'మీరు తదుపరి స్థాయికి వెళ్లడానికి కనీసం 80% స్కోర్ అవసరం' : + selectedLanguage === 'mr' ? + 'पुढील स्तरावर जाण्यासाठी किमान 80% स्कोअर आवश्यक आहे' : + selectedLanguage === 'kn' ? + 'ಮುಂದಿನ ಹಂತಕ್ಕೆ ಹೋಗಲು ನಿಮಗೆ ಕನಿಷ್ಠ 80% ಅಗತ್ಯವಿದೆ' : + 'You need at least 80% to advance to the next level' + } +

+ } +
+ +
+ + +
+
+ +
+ ); + + if (useSpaceBackground) { + return ( + + {content} + + ); + } + + return content; +} diff --git a/src/lib/axl-explorations/src/components/TryAgainScreen.tsx b/src/lib/axl-explorations/src/components/TryAgainScreen.tsx new file mode 100644 index 00000000..6e94b436 --- /dev/null +++ b/src/lib/axl-explorations/src/components/TryAgainScreen.tsx @@ -0,0 +1,67 @@ +import { Button } from "./ui/button"; +import { Card } from "./ui/card"; +import { ArrowLeft } from "lucide-react"; + +interface TryAgainScreenProps { + onTryAgain: () => void; + onBack: () => void; +} + +export function TryAgainScreen({ onTryAgain, onBack }: TryAgainScreenProps) { + return ( +
+
+ {/* Header */} +
+ + +
+

+ Try Again! +

+
+ +
+
+ + {/* Try Again Card */} + +
+
+ 😔 +
+

+ Oops! Time's Up +

+

+ Don't worry, you can try again and improve your score! +

+
+ +
+ + +
+
+
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/UserStats.tsx b/src/lib/axl-explorations/src/components/UserStats.tsx new file mode 100644 index 00000000..da9d4f5b --- /dev/null +++ b/src/lib/axl-explorations/src/components/UserStats.tsx @@ -0,0 +1,71 @@ +import { Card } from "./ui/card"; +import { Star, Target, TrendingUp } from "lucide-react"; +import { cn } from "../lib/utils"; + +interface UserStatsProps { + totalStars: number; + dayStreak: number; + currentLevel: number; + className?: string; +} + +export function UserStats({ + totalStars, + dayStreak, + currentLevel, + className +}: UserStatsProps) { + const stats = [ + { + icon: Star, + label: "Total Stars", + value: totalStars, + color: "text-warning", + bgColor: "bg-warning/10" + }, + { + icon: Target, + label: "Day Streak", + value: dayStreak, + color: "text-error", + bgColor: "bg-error/10" + }, + { + icon: TrendingUp, + label: "Current Level", + value: `Level ${currentLevel}`, + color: "text-blue-game", + bgColor: "bg-blue-game/10" + } + ]; + + return ( +
+ {stats.map((stat, index) => ( + +
+
+ +
+ +
+
+ {stat.value} +
+ +
+ {stat.label} +
+
+
+
+ ))} +
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/CollectBadgeGame.tsx b/src/lib/axl-explorations/src/components/games/CollectBadgeGame.tsx new file mode 100644 index 00000000..9cf1e801 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CollectBadgeGame.tsx @@ -0,0 +1,355 @@ +import { useState, useEffect, useCallback } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { ArrowLeft, Timer, Star } from "lucide-react"; +import { TryAgainScreen } from "../TryAgainScreen"; +import { GameCompleteScreen } from "../GameCompleteScreen"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language } from "../../constants/languages"; + +interface CardData { + id: number; + letter: string; + isFlipped: boolean; + isMatched: boolean; +} + +interface GameResult { + step: number; + pairsFound: number; + movesUsed: number; + timeTaken: number; + score: number; +} + +interface CollectBadgeGameProps { + onBack: () => void; +} + +export function CollectBadgeGame({ onBack }: CollectBadgeGameProps) { + const { selectedLanguage } = useLanguage(); + const [cards, setCards] = useState([]); + const [flippedCards, setFlippedCards] = useState([]); + const [moves, setMoves] = useState(0); + const [pairsFound, setPairsFound] = useState(0); + const [score, setScore] = useState(0); + const [timeRemaining, setTimeRemaining] = useState(300); // 5 minutes = 300 seconds + const [isGameComplete, setIsGameComplete] = useState(false); + const [isGameFailed, setIsGameFailed] = useState(false); + const [gameResult, setGameResult] = useState(null); + const [gameStarted, setGameStarted] = useState(true); // Start game immediately + const [gameStartTime, setGameStartTime] = useState(0); + const [showStartOverlay, setShowStartOverlay] = useState(true); // Show start overlay + + // Generate letters for 4x4 grid (16 cards, 8 pairs) + const generateLetters = (): string[] => { + const baseLetters: Record = { + 'en': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'], + 'te': ['అ', 'ఆ', 'ఇ', 'ఈ', 'ఉ', 'ఊ', 'ఋ', 'ఎ', 'ఏ', 'ఐ', 'ఒ', 'ఓ', 'ఔ', 'క', 'ఖ', 'గ', 'ఘ', 'ఙ', 'చ', 'ఛ', 'జ', 'ఝ', 'ఞ', 'ట', 'ఠ', 'డ'], + 'mr': ['अ', 'आ', 'इ', 'ई', 'उ', 'ऊ', 'ऋ', 'ए', 'ऐ', 'ओ', 'औ', 'क', 'ख', 'ग', 'घ', 'ङ', 'च', 'छ', 'ज', 'झ', 'ञ', 'ट', 'ठ', 'ड', 'ढ', 'ण'], + 'kn': ['ಅ', 'ಆ', 'ಇ', 'ಈ', 'ಉ', 'ಊ', 'ಋ', 'ಎ', 'ಏ', 'ಐ', 'ಒ', 'ಓ', 'ಔ', 'ಕ', 'ಖ', 'ಗ', 'ಘ', 'ಙ', 'ಚ', 'ಛ', 'ಜ', 'ಝ', 'ಞ', 'ಟ', 'ಠ', 'ಡ'] + }; + + const letters = baseLetters[selectedLanguage]; + + // 4x4 grid = 16 cards = 8 pairs + const uniquePairs = 8; + + // Take only the number of unique letters needed for pairs + const stepLetters = letters.slice(0, uniquePairs); + const pairedLetters = [...stepLetters, ...stepLetters]; // Duplicate for pairs + + return pairedLetters; + }; + + // Initialize cards for 4x4 grid + const initializeCards = useCallback(() => { + const letters = generateLetters(); + const shuffledLetters = letters.sort(() => Math.random() - 0.5); + + console.log(`Initializing 4x4 grid: 8 pairs, ${letters.length} cards`); + + const newCards: CardData[] = shuffledLetters.map((letter, index) => ({ + id: index, + letter, + isFlipped: false, + isMatched: false + })); + + setCards(newCards); + setFlippedCards([]); + setMoves(0); + setPairsFound(0); + }, [selectedLanguage]); + + // Start game function + const startGame = () => { + setShowStartOverlay(false); + setGameStartTime(Date.now()); + setTimeRemaining(300); // Reset timer to 5 minutes + }; + + // Reset game function + const resetGame = () => { + setCards([]); + setFlippedCards([]); + setMoves(0); + setPairsFound(0); + setScore(0); + setTimeRemaining(300); + setIsGameComplete(false); + setIsGameFailed(false); + setGameResult(null); + setShowStartOverlay(true); // Show start overlay so user can click "Start Game" + initializeCards(); // Reinitialize cards with new shuffle + }; + + // Initialize cards immediately when component mounts + useEffect(() => { + initializeCards(); + }, [initializeCards]); + + // Handle card click + const handleCardClick = (cardId: number) => { + if (flippedCards.length >= 2 || cards[cardId].isFlipped || cards[cardId].isMatched) { + return; + } + + const newFlippedCards = [...flippedCards, cardId]; + setFlippedCards(newFlippedCards); + + setCards(prevCards => + prevCards.map(card => + card.id === cardId ? { ...card, isFlipped: true } : card + ) + ); + + if (newFlippedCards.length === 2) { + setMoves(prev => prev + 1); + + const [firstId, secondId] = newFlippedCards; + const firstCard = cards[firstId]; + const secondCard = cards[secondId]; + + if (firstCard.letter === secondCard.letter) { + // Match found + setTimeout(() => { + setCards(prevCards => + prevCards.map(card => + card.id === firstId || card.id === secondId + ? { ...card, isMatched: true, isFlipped: true } + : card + ) + ); + setPairsFound(prev => { + const newCount = prev + 1; + console.log(`Match found! Pairs found: ${newCount}`); + return newCount; + }); + setScore(prev => prev + 1); + setFlippedCards([]); + }, 500); + } else { + // No match + setTimeout(() => { + setCards(prevCards => + prevCards.map(card => + card.id === firstId || card.id === secondId + ? { ...card, isFlipped: false } + : card + ) + ); + setFlippedCards([]); + }, 1000); + } + } + }; + + // Check if game is complete + useEffect(() => { + const totalPairs = 8; // 4x4 grid = 8 pairs + + console.log(`Game: Pairs found: ${pairsFound}/${totalPairs}`); + + if (pairsFound === totalPairs && gameStarted) { + console.log(`Game completed!`); + const gameTime = Math.floor((Date.now() - gameStartTime) / 1000); + const result: GameResult = { + step: 1, + pairsFound, + movesUsed: moves, + timeTaken: gameTime, + score: score + }; + + setGameResult(result); + + // Game complete + setTimeout(() => { + setIsGameComplete(true); + }, 1500); + } + }, [pairsFound, gameStarted, moves, score, gameStartTime]); + + // Timer effect + useEffect(() => { + if (!showStartOverlay && !isGameComplete && !isGameFailed) { + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + setIsGameFailed(true); + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } + }, [showStartOverlay, isGameComplete, isGameFailed]); + + // Format time + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + // Format time in minutes only + const formatTimeInMinutes = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + if (mins === 0) { + return `${secs}s`; + } else if (secs === 0) { + return `${mins}m`; + } else { + return `${mins}m ${secs}s`; + } + }; + + + + + // Show game failed screen + if (isGameFailed) { + return ; + } + + // Show game complete screen + if (isGameComplete) { + return ; + } + + // Main game interface + return ( +
+
+ {/* Header */} +
+ + +
+

+ Collect Badge Game +

+
+ Memory Card Challenge +
+
+ +
+
+ + {/* Main Content Card */} + + {/* Game Stats and Timer - Top Section */} +
+
+
+
+ + {score} + Score +
+
+ {pairsFound} + Pairs +
+
+ {moves} + Moves +
+
+ + {/* Timer Section - Right aligned */} +
+
+ +
+
+
+
+ + {/* Game Content - Compact spacing */} +
+
+

Memory Game

+

+ Find 8 matching pairs +

+
+ + {/* Card Grid - 4x4 with responsive spacing */} +
+ {cards.map((card) => ( + + ))} +
+ + {/* Start Button - Responsive positioning */} + {showStartOverlay && ( +
+ +
+ )} +
+
+
+
+ ); +} + +export default CollectBadgeGame; \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/CombinedLetterGames.tsx b/src/lib/axl-explorations/src/components/games/CombinedLetterGames.tsx new file mode 100644 index 00000000..ba6f79e8 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CombinedLetterGames.tsx @@ -0,0 +1,1491 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ProgressBar } from "../ProgressBar"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Globe, Eye, EyeOff, Volume2, SkipForward, Timer, Sparkles, BookOpen, Brain, CheckCircle } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { memoryGameDataLoader } from "../../utils/memoryGameDataLoader"; +import { gameSessionTracker } from "../../utils/gameSessionTracker"; +import { sessionManager } from "../../utils/sessionManager"; +import { sunbirdTelemetryService, createGameSessionData, createQuestionResponseData, createGameEndSessionData, type GameSessionData } from "../../utils/sunbirdTelemetryService"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { teluguAudioManager } from "../../utils/teluguAudioManager"; +import { kannadaAudioManager } from "../../utils/kannadaAudioManager"; +import { marathiAudioManager } from "../../utils/marathiAudioManager"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, LANGUAGES as SHARED_LANGUAGES } from "../../constants/languages"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { CombinedLetterGamesPreview } from "./CombinedLetterGamesPreview"; +import { LetterHuntGameCore } from "./LetterHuntGameCore"; +import { ROARRapidVisualGameCore } from "./ROARRapidVisualGameCore"; +import { MemoryGameCore } from "./MemoryGameCore"; +type GameType = 'letterHunt' | 'quickSight' | 'memoryChallenge'; + +interface LanguageOption { + code: Language; + name: string; + nativeName: string; + flag: string; +} + +// Get languages from JSON data +const LANGUAGES: LanguageOption[] = memoryGameDataLoader.getLanguages(); + +interface CombinedQuestion { + id: string; + type: GameType; + target: string; + options?: string[]; + letters?: string[]; + targetPosition?: number; + sequence?: string[]; + audio: string; + audioText: string; + language: Language; + complexity: string; +} + +interface CombinedLetterGamesProps { + onBack: () => void; +} + +function CombinedLetterGames({ onBack }: CombinedLetterGamesProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [timeRemaining, setTimeRemaining] = useState(0); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [showSequence, setShowSequence] = useState(true); + const [userInput, setUserInput] = useState([]); + const [sequenceTimer, setSequenceTimer] = useState(3); + const [levelFailed, setLevelFailed] = useState(false); + const [showQuickSightTarget, setShowQuickSightTarget] = useState(true); + const [currentLetterOptions, setCurrentLetterOptions] = useState([]); + const [showQuickSightOptions, setShowQuickSightOptions] = useState(false); + const [showTimeoutMessage, setShowTimeoutMessage] = useState(false); + // Telemetry state + const [telemetrySessionData, setTelemetrySessionData] = useState(null); + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations + const getLanguageLevels = (language: Language) => { + switch (language) { + case 'te': + return { + maxLevels: 10, // Standardized to 10 levels for Telugu + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + case 'mr': + return { + maxLevels: 10, // Standardized to 10 levels for Marathi + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + case 'kn': + return { + maxLevels: 10, // Standardized to 10 levels for Kannada + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + default: + return { + maxLevels: 10, // Standard levels for English + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + } + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `combinedLetter_${selectedLanguage}` : 'combinedLetter'; + + // Get current user and session-based level + const currentUser = sessionManager.getCurrentUser(); + const sessionGameProgress = currentUser ? gameSessionTracker.getGameProgress(currentUser.username, gameKey) : null; + const sessionCurrentLevel = sessionGameProgress ? gameSessionTracker.getCurrentLevel(currentUser.username, gameKey) : 1; + + // Use session-based level instead of old progress system + const currentLevel = selectedLevel || sessionCurrentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Extract metadata (currentLevel from backend) + if (result.metadata?.currentLevel) { + setBackendCurrentLevel(result.metadata.currentLevel); + } + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + setShowPreview(false); // Hide preview when level is selected + setIsGameComplete(false); + setScore(0); + setTotalCorrect(0); + setCurrentQuestionIndex(0); + setSelectedAnswer(null); + setShowFeedback(false); + setLevelFailed(false); + } + }, [selectedLevel, selectedLanguage]); + + // Get language letters from JSON data - now includes matras and compound letters + // Note: memoryGameDataLoader only supports en, te, mr, kn (not hi) + const getLanguageLetters = (language: Language): string[] => { + // Ensure only supported languages are passed (fallback to 'en' if somehow 'hi' is passed) + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + return memoryGameDataLoader.getAllLetters(supportedLanguage); + }; + + // Get level progression percentages + const getLevelProgression = (level: number) => { + if (level <= 4) { + return { + letterHunt: 50, + quickSight: 30, + memoryChallenge: 20 + }; + } else if (level <= 7) { + return { + letterHunt: 30, + quickSight: 30, + memoryChallenge: 40 + }; + } else { + return { + letterHunt: 20, + quickSight: 20, + memoryChallenge: 60 + }; + } + }; + + // Generate Letter Hunt questions with proper level-based letter sets + const generateLetterHuntQuestions = (language: Language, level: number, count: number): CombinedQuestion[] => { + // Get level-appropriate letter set from JSON data + const getLevelLetters = (language: Language, level: number): string[] => { + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // For Telugu, Kannada, and Marathi, use exact level mapping + if (supportedLanguage === 'te' || supportedLanguage === 'kn' || supportedLanguage === 'mr') { + const levelKey = level.toString(); + return memoryGameDataLoader.getLettersByLevel(supportedLanguage, levelKey); + } + + // For other languages, map 10 levels to complexity levels + if (level <= 2) return memoryGameDataLoader.getLetters(supportedLanguage, 'basic'); + if (level <= 4) return memoryGameDataLoader.getLetters(supportedLanguage, 'intermediate'); + if (level <= 6) return memoryGameDataLoader.getLetters(supportedLanguage, 'advanced'); + if (level <= 8) return memoryGameDataLoader.getLetters(supportedLanguage, 'expert'); + return memoryGameDataLoader.getLetters(supportedLanguage, 'master'); + }; + + const lettersToUse = getLevelLetters(language, level); + const questions: CombinedQuestion[] = []; + + for (let i = 0; i < count; i++) { + const target = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + const options = [target]; + + while (options.length < 4) { + const randomLetter = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + if (!options.includes(randomLetter)) { + options.push(randomLetter); + } + } + + // Shuffle options + for (let j = options.length - 1; j > 0; j--) { + const k = Math.floor(Math.random() * (j + 1)); + [options[j], options[k]] = [options[k], options[j]]; + } + + questions.push({ + id: `lh_${i}`, + type: 'letterHunt', + target, + options, + audio: target, + audioText: target, + language, + complexity: difficultySettings.complexity + }); + } + + return questions; + }; + + // Generate Quick Sight questions with proper level-based letter sets + const generateQuickSightQuestions = (language: Language, level: number, count: number): CombinedQuestion[] => { + // Get level-appropriate letter set from JSON data + const getLevelLetters = (language: Language, level: number): string[] => { + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // For Telugu and Kannada, use exact level mapping + if (supportedLanguage === 'te' || supportedLanguage === 'kn') { + const levelKey = level.toString(); + return memoryGameDataLoader.getLettersByLevel(supportedLanguage, levelKey); + } + + // For other languages, map 10 levels to complexity levels + if (level <= 2) return memoryGameDataLoader.getLetters(supportedLanguage, 'basic'); + if (level <= 4) return memoryGameDataLoader.getLetters(supportedLanguage, 'intermediate'); + if (level <= 6) return memoryGameDataLoader.getLetters(supportedLanguage, 'advanced'); + if (level <= 8) return memoryGameDataLoader.getLetters(supportedLanguage, 'expert'); + return memoryGameDataLoader.getLetters(supportedLanguage, 'master'); + }; + + const availableLetters = getLevelLetters(language, level); + const questions: CombinedQuestion[] = []; + + // For Quick Sight, we need at least 6 unique letters + let lettersToUse = availableLetters; + if (lettersToUse.length < 6) { + // If not enough letters, use all available + lettersToUse = availableLetters; + } else { + // Use a subset for variety, but ensure we have enough + lettersToUse = availableLetters.slice(0, Math.min(18, availableLetters.length)); + } + + console.log('Letters to use:', lettersToUse); + console.log('Letters to use length:', lettersToUse.length); + + const usedTargets: string[] = []; + + for (let i = 0; i < count; i++) { + // Get a random target letter that hasn't been used + let target; + let attempts = 0; + + do { + target = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + attempts++; + } while (usedTargets.includes(target) && attempts < 20); + + // Fallback if we can't find a unique target + if (attempts >= 20) { + console.warn(`Could not find unique target after ${attempts} attempts, using first available letter`); + target = lettersToUse[0]; + } + + usedTargets.push(target); + + // Create 6 letter positions with target in random position + const targetPosition = Math.floor(Math.random() * 6); + const letters = Array(6).fill(''); + + // Place target letter + letters[targetPosition] = target; + + // Fill other positions with smart confusing letters from JSON (avoiding target) + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const confusingLetters = memoryGameDataLoader.getConfusingLetters(supportedLanguage, target); + const usedDistractors = new Set(); + + for (let j = 0; j < 6; j++) { + if (j !== targetPosition) { + let randomLetter; + let distractorAttempts = 0; + + // First try to use confusing letters from JSON + if (confusingLetters.length > 0 && usedDistractors.size < confusingLetters.length) { + const availableConfusing = confusingLetters.filter(letter => + letter !== target && + !letters.includes(letter) && + !usedDistractors.has(letter) + ); + + if (availableConfusing.length > 0) { + randomLetter = availableConfusing[Math.floor(Math.random() * availableConfusing.length)]; + usedDistractors.add(randomLetter); + } + } + + // Fallback to random letters if no confusing letters available + if (!randomLetter) { + do { + randomLetter = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + distractorAttempts++; + } while ((randomLetter === target || letters.includes(randomLetter)) && distractorAttempts < 10); + + // Final fallback + if (distractorAttempts >= 10) { + randomLetter = lettersToUse.find(l => l !== target && !letters.includes(l)) || lettersToUse[0]; + } + } + + letters[j] = randomLetter; + } + } + + console.log(`Question ${i + 1}: Final sequence =`, letters, 'Target position =', targetPosition); + + questions.push({ + id: `qs_${i}`, + type: 'quickSight', + target, + letters: letters, + targetPosition, + audio: target, + audioText: target, + language, + complexity: difficultySettings.complexity + }); + } + + return questions; + }; + + // Generate Memory Challenge questions using JSON data + const generateMemoryChallengeQuestions = (language: Language, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Use JSON data loader to generate memory sequences + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const memorySequences = memoryGameDataLoader.generateMemoryQuestions( + supportedLanguage, + level, + difficultySettings.complexity, + count + ); + + for (let i = 0; i < memorySequences.length; i++) { + const sequence = memorySequences[i]; + + questions.push({ + id: `mc_${i}`, + type: 'memoryChallenge', + target: sequence.sequence.join(''), + sequence: sequence.sequence, + audio: sequence.audioText, + audioText: sequence.audioText, + language, + complexity: sequence.complexity + }); + } + + return questions; + }; + + // Generate combined questions based on level progression + const generateCombinedQuestions = (language: Language, level: number): CombinedQuestion[] => { + const progression = getLevelProgression(level); + const totalQuestions = 10; + + const letterHuntCount = Math.round((progression.letterHunt / 100) * totalQuestions); + const quickSightCount = Math.round((progression.quickSight / 100) * totalQuestions); + const memoryChallengeCount = totalQuestions - letterHuntCount - quickSightCount; + + const letterHuntQuestions = generateLetterHuntQuestions(language, level, letterHuntCount); + const quickSightQuestions = generateQuickSightQuestions(language, level, quickSightCount); + const memoryChallengeQuestions = generateMemoryChallengeQuestions(language, level, memoryChallengeCount); + + // Combine questions in order: Letter Hunt first, then Quick Sight, then Memory Challenge + const allQuestions = [...letterHuntQuestions, ...quickSightQuestions, ...memoryChallengeQuestions]; + + return allQuestions; + }; + + // Initialize game session and questions + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + + // Add a small delay to ensure state reset completes first + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(selectedLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); // Initialize for first question + setQuestionSummaries([]); + + // Start session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.startLevelSession( + currentUser.username, + gameKey, + 'Combined Letter Games', + selectedLevel + ); + + // End any existing subsession before starting a new one + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + + // Start telemetry subsession (only once per level) + await sessionTelemetryManager.startSubSession(gameKey, selectedLevel, selectedLanguage); + } + + const newQuestions = generateCombinedQuestions(selectedLanguage, selectedLevel); + + // Initialize telemetry session data (for backward compatibility) + if (currentUser) { + const gameSessionData = createGameSessionData( + gameKey, + "Combined Letter Games", + "combinedLetter", + selectedLevel, + selectedLanguage, + difficultySettings.complexity, + currentUser.username, + true // isCombinedGame + ); + setTelemetrySessionData(gameSessionData); + } + setQuestions(newQuestions); + + // Initialize user input array for memory challenge + setUserInput([]); + setShowSequence(true); + } + }; + + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Reset game state when URL changes + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setShowTimeoutMessage(false); + setUserInput([]); + setShowSequence(true); + setShowQuickSightTarget(true); + setShowQuickSightOptions(false); + setCurrentLetterOptions([]); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + } + }, [selectedLevel]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + const currentQuestion = questions[currentQuestionIndex]; + // Track question start time for telemetry + useEffect(() => { + if (currentQuestion) { + setQuestionStartTime(Date.now()); + } + }, [currentQuestionIndex]); + + // Debug current question + useEffect(() => { + if (currentQuestion) { + console.log('Current question:', currentQuestion); + if (currentQuestion.type === 'quickSight') { + console.log('Quick Sight question details:', { + target: currentQuestion.target, + letters: currentQuestion.letters, + targetPosition: currentQuestion.targetPosition, + lettersLength: currentQuestion.letters?.length, + lettersType: typeof currentQuestion.letters, + lettersIsArray: Array.isArray(currentQuestion.letters) + }); + } + } + }); + const handleTimeUp = useCallback(() => { + setIsTimerRunning(false); + setSelectedAnswer(-1); // -1 indicates time up + setIsCorrect(false); + setShowFeedback(false); // Don't show feedback immediately + setShowTimeoutMessage(true); // Show timeout message + + // Send telemetry RESPONSE event for timeout + // if (telemetrySessionData) { + // const responseTime = Date.now() - questionStartTime; + // const responseData = createQuestionResponseData( + // currentQuestion.id, + // currentQuestion.type, + // -1, // timeout answer + // currentQuestion.target, + // false, // incorrect due to timeout + // responseTime, + // 1, // attempts + // currentQuestion.complexity + // ); + // telemetryService.sendResponseEvent(telemetrySessionData, responseData); + // } + + recordAnswer(false); + + // For Quick Sight, hide target letter and show selection options + if (currentQuestion && currentQuestion.type === 'quickSight') { + setShowQuickSightTarget(false); + setShowQuickSightOptions(true); + } + }, [currentQuestion]); + + // Timer functionality for Quick Sight (same as original) + useEffect(() => { + if (currentQuestion && currentQuestion.type === 'quickSight' && !showFeedback && questions.length > 0) { + // Always reset timer state for new question + const timeLimit = getTimeLimit(difficultySettings.complexity); + setTimeRemaining(timeLimit); + setIsTimerRunning(true); + setShowQuickSightTarget(true); + setShowQuickSightOptions(false); + setShowTimeoutMessage(false); // Reset timeout message for new question + + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + // Time's up - auto-submit wrong answer + handleTimeUp(); + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => { + clearInterval(timer); + setIsTimerRunning(false); + }; + } + }, [currentQuestionIndex, currentQuestion, showFeedback, difficultySettings.complexity, questions.length]); + + // Get time limit for Quick Sight and Memory Challenge (same as MemoryGame) + const getTimeLimit = (complexity: string) => { + // Base time limits by complexity + const baseTime = { + basic: 8, + intermediate: 6, + advanced: 5, + expert: 4, + master: 3 + }; + + // Additional level-based time reduction for higher levels + const levelBonus = Math.max(0, Math.floor((currentLevel - 1) * 0.2)); + + return Math.max(3, baseTime[complexity as keyof typeof baseTime] - levelBonus); + }; + + // Enhanced audio function for different languages + const playAudio = async (text: string, language: Language) => { + // For Telugu, try to use local audio files first + if (language === 'te') { + const audioPlayed = await teluguAudioManager.playAudio(text); + if (audioPlayed) { + return; // Successfully played audio file + } + } + + // For Kannada, try to use local audio files first + if (language === 'kn') { + const audioPlayed = await kannadaAudioManager.playAudio(text); + if (audioPlayed) { + return; // Successfully played audio file + } + } + + // For Marathi, try to use local audio files first + if (language === 'mr') { + const audioPlayed = await marathiAudioManager.playAudio(text); + if (audioPlayed) { + return; // Successfully played audio file + } + } + + // Fallback to TTS for all languages + playTTSAudio(text, language); + }; + + const playTTSAudio = (text: string, language: Language) => { + const utterance = new SpeechSynthesisUtterance(text); + + // Language-specific settings + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 0.6; // Slower for Telugu + utterance.pitch = 0.9; + utterance.volume = 0.9; + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 0.6; // Slower for Marathi + utterance.pitch = 0.9; + utterance.volume = 0.9; + break; + case 'kn': + utterance.lang = 'kn-IN'; + utterance.rate = 0.6; // Slower for Kannada + utterance.pitch = 0.9; + utterance.volume = 0.9; + break; + default: + utterance.lang = 'en-US'; + utterance.rate = 0.8; + utterance.pitch = 1.0; + utterance.volume = 0.8; + } + + // Try to find the best voice (with fallback for voice loading) + const findAndSetVoice = () => { + const voices = speechSynthesis.getVoices(); + let bestVoice = null; + + if (voices.length === 0) return; + + if (language === 'te') { + bestVoice = voices.find(voice => + voice.lang.includes('te') || + voice.lang.includes('hi-IN') || + voice.lang.includes('en-IN') + ); + } else if (language === 'mr') { + bestVoice = voices.find(voice => + voice.lang.includes('mr') || + voice.lang.includes('hi-IN') || + voice.lang.includes('en-IN') + ); + } else if (language === 'kn') { + bestVoice = voices.find(voice => + voice.lang.includes('kn') || + voice.lang.includes('hi-IN') || + voice.lang.includes('en-IN') + ); + } else { + bestVoice = voices.find(voice => + voice.lang.includes('en-US') || + voice.lang.includes('en-GB') + ); + } + + if (bestVoice) { + utterance.voice = bestVoice; + } + }; + + findAndSetVoice(); + speechSynthesis.speak(utterance); + }; + + // Auto-play / setup for memory challenge only; Letter Hunt audio is handled by its core + useEffect(() => { + if (currentQuestion && !showFeedback && selectedLanguage && + selectedLevel !== null && !showLevelSelector) { + const timer = setTimeout(() => { + // For memory challenge, show sequence for 3 seconds then hide + if (currentQuestion.type === 'memoryChallenge') { + setShowSequence(true); + setSequenceTimer(3); + setUserInput([]); + } + }, 300); + + return () => clearTimeout(timer); + } + }, [currentQuestionIndex, currentQuestion, showFeedback, selectedLanguage, selectedLevel, showLevelSelector]); + + // Generate question-specific letter options (correct letters + distractors) - same as MemoryGame + const generateQuestionLetterOptions = (sequence: string[]) => { + const correctLetters = [...sequence]; + const numDistractors = correctLetters.length; // Same number of distractors as correct letters + + // Get all available letters for the language + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const availableLetters = selectedLanguage ? memoryGameDataLoader.getAllLetters(supportedLanguage) : []; + + // Filter out correct letters to get potential distractors + const potentialDistractors = availableLetters.filter(letter => !correctLetters.includes(letter)); + + // Shuffle and select distractors + const shuffledDistractors = potentialDistractors.sort(() => Math.random() - 0.5); + const selectedDistractors = shuffledDistractors.slice(0, numDistractors); + + // Combine correct letters and distractors, then shuffle + const allOptions = [...correctLetters, ...selectedDistractors]; + return allOptions.sort(() => Math.random() - 0.5); + }; + + // Generate letter options when question changes - same as MemoryGame + useEffect(() => { + if (currentQuestion && currentQuestion.type === 'memoryChallenge' && !showSequence) { + const newOptions = generateQuestionLetterOptions(currentQuestion.sequence || []); + setCurrentLetterOptions(newOptions); + } + }, [currentQuestionIndex, currentQuestion, showSequence, selectedLanguage]); + + // Timer for showing sequence (same as MemoryGame) + useEffect(() => { + if (currentQuestion && currentQuestion.type === 'memoryChallenge' && showSequence && !showFeedback && questions.length > 0) { + // Start timer when showing sequence + const timeLimit = getTimeLimit(difficultySettings.complexity); + setTimeRemaining(timeLimit); + setIsTimerRunning(true); + setShowTimeoutMessage(false); // Reset timeout message for new question + + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + // Time's up - automatically move to input phase + setIsTimerRunning(false); + setShowSequence(false); + setSequenceTimer(3); + setShowTimeoutMessage(true); // Show timeout message + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => { + clearInterval(timer); + setIsTimerRunning(false); + }; + } + }, [currentQuestionIndex, currentQuestion, showSequence, showFeedback, difficultySettings.complexity, questions.length]); + + // Handle different game type answers (same logic as original games) + const handleAnswer = async (answer: string | number) => { + if (showFeedback) return; + + setSelectedAnswer(answer); + let correct = false; + + switch (currentQuestion.type) { + case 'letterHunt': + correct = answer === currentQuestion.target; + break; + case 'quickSight': + if (!currentQuestion) return; + setIsTimerRunning(false); + correct = answer === currentQuestion.targetPosition; + if (correct) { + setScore(prevScore => prevScore + 1); + } + break; + case 'memoryChallenge': + correct = answer === currentQuestion.target; + break; + } + + setIsCorrect(correct); + setShowFeedback(true); + setShowTimeoutMessage(false); + + // Send telemetry ASSESS event + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + + // For quick sight, send the actual letter instead of position index + let telemetryAnswer = answer; + if (currentQuestion.type === 'quickSight' && typeof answer === 'number' && currentQuestion.letters) { + telemetryAnswer = currentQuestion.letters[answer] || answer; + } + + await sessionTelemetryManager.sendAssessEvent( + currentQuestion.id, + currentQuestion.type, + telemetryAnswer, + currentQuestion.target, + correct, + responseTime + ); + + // Update subsession with question attempt + sessionTelemetryManager.updateSubSession(correct); + recordAnswer(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: currentQuestion.id, + questionType: currentQuestion.type, + userAnswer: telemetryAnswer, + correctAnswer: currentQuestion.target, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + // Update session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.updateLevelSession( + currentUser.username, + gameKey, + currentLevel, + currentQuestionIndex + 1, + totalCorrect + (correct ? 1 : 0) + ); + } + + if (correct && currentQuestion.type !== 'quickSight') { + setScore(prevScore => prevScore + 10); + setTotalCorrect(prevTotal => prevTotal + 1); + } else if (correct && currentQuestion.type === 'quickSight') { + setTotalCorrect(prevTotal => prevTotal + 1); + } + + // Don't auto-advance - player must manually continue (same as original games) + // This ensures they see feedback and can't skip questions + }; + + const handleContinue = useCallback(async () => { + // Player manually continues to next question (no retry for Memory Challenge) + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedAnswer(null); + setShowFeedback(false); + setUserInput([]); + setShowSequence(true); + setSequenceTimer(3); + setTimeRemaining(0); + setIsTimerRunning(false); + setShowQuickSightTarget(true); + setShowQuickSightOptions(false); + setCurrentLetterOptions([]); + setShowTimeoutMessage(false); // Reset timeout message for next question + } else { + // Level complete - check if player can advance + // Calculate score percentage for level completion + const scorePercentage = (totalCorrect / questions.length) * 100; + const canAdvance = scorePercentage >= 80; // Minimum 80% to advance + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); // Convert to seconds + + // End session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.endLevelSession( + currentUser.username, + gameKey, + currentLevel, + questions.length, + totalCorrect + ); + + // Send tracking assessment data to backend + console.log('📊 Sending tracking assessment data for level completion...'); + + // Get current session and subsession IDs from telemetry manager + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + // Use ref or capture latest state to ensure all questions are included + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Letter Games', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: !canAdvance, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + // Session tracker handles level advancement automatically + // Check if level was advanced by comparing with previous level + if (currentUser) { + const newSessionProgress = gameSessionTracker.getGameProgress(currentUser.username, gameKey); + if (newSessionProgress && newSessionProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + } + + // Show success screen for all completions (like individual games) + setLevelFailed(!canAdvance); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, gameKey, currentLevel]); + + const addLetterToInput = (letter: string) => { + if (userInput.length < (currentQuestion.sequence?.length || 0)) { + setUserInput([...userInput, letter]); + } + }; + + const removeLastLetter = () => { + setUserInput(userInput.slice(0, -1)); + }; + + const checkSequence = async () => { + const correct = JSON.stringify(userInput) === JSON.stringify(currentQuestion.sequence); + setIsCorrect(correct); + setShowFeedback(true); + setShowTimeoutMessage(false); + + // Send telemetry ASSESS event + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + await sessionTelemetryManager.sendAssessEvent( + currentQuestion.id, + currentQuestion.type, + userInput.join(""), + currentQuestion.target, + correct, + responseTime + ); + + // Update subsession with question attempt + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: currentQuestion.id, + questionType: currentQuestion.type, + userAnswer: userInput.join(""), + correctAnswer: currentQuestion.target, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + // Record answer for progress tracking + recordAnswer(correct); + + if (correct) { + setScore(prevScore => prevScore + 30); + setTotalCorrect(prevTotal => prevTotal + 1); + } + + // Don't auto-advance - player must manually click "Next" button + // This ensures they see feedback and can't skip questions + + // Don't auto-advance - player must manually click "Next" button + // This ensures they see feedback and can't skip questions + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setUserInput([]); + setShowSequence(true); + setShowQuickSightTarget(true); + setShowQuickSightOptions(false); + setCurrentLetterOptions([]); + setShowTimeoutMessage(false); // Reset timeout message + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateCombinedQuestions(selectedLanguage, selectedLevel); + setQuestions(newQuestions); + } + }; + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/combined-letter-games/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/combined-letter-games'); + }; + + const handleCollectBadge = () => { + navigate('/combined-letter-games/collect-badge'); + }; + + // Handle back button with telemetry + const handleBackWithTelemetry = async () => { + // Only send telemetry if we're in the middle of a level (not level selector) + if (selectedLevel !== null && !showLevelSelector && !isGameComplete) { + console.log('📊 Back button clicked during level play - sending telemetry events'); + + // End current telemetry subsession with back button context + await sessionTelemetryManager.endSubSessionWithBackButton(); + + // Update session tracking to mark level as incomplete + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.endLevelSession( + currentUser.username, + gameKey, + currentLevel, + currentQuestionIndex, // Questions attempted so far + totalCorrect + ); + } + } + + // Call the original onBack function + onBack(); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; + const percentage = (totalCorrect / questions.length) * 100; + + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; // Below 50% = 0 stars + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Letter Master - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Quick Learner - Great Progress!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) - only if backend level is 1, OR if forcePreview is true (for demo) + if (showPreview && selectedLanguage && (backendCurrentLevel === 1 || forcePreview)) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selection screen + if (showLevelSelector) { + // Check if there's a failed level stored in localStorage for this game + const failedLevelKey = `failedLevel_${gameKey}`; + const failedLevel = localStorage.getItem(failedLevelKey); + + // Use the actual current level being played + // Priority: failedLevel > sessionCurrentLevel + const levelSelectorCurrentLevel = failedLevel ? parseInt(failedLevel) : sessionCurrentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Combined Letter Games" + showBadge={true} + onCollectBadge={handleCollectBadge} + badgeTooltip="Memory Card Challenge" + gameKey={gameKey} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + l.code === selectedLanguage)?.nativeName}`} + score={totalCorrect} + totalQuestions={questions.length} + starsEarned={calculateStars()} + newAchievements={getNewAchievements()} + onPlayAgain={resetGame} + onBackToHub={onBack} + hasNextLevel={currentLevel < languageLevels.maxLevels} + onNextLevel={() => { + const nextLevel = currentLevel + 1; + // Navigate to the next level + navigate(`/combined-letter-games/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
+
+
+

Loading Combined Letter Games...

+

Setting up your questions...

+
+
+
+
+ ); + } + + // Render game interface based on question type (same UI as original games) + return ( +
+
+ {/* Header */} +
+ + +
+

+ Combined Letter Games +

+
+ + + {selectedLevel !== null && selectedLevel !== sessionCurrentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Timer with Clockwise Timer for Quick Sight and Memory Challenge - Hide when time is up or feedback is shown */} + {currentQuestion && ( + (currentQuestion.type === 'quickSight') || + (currentQuestion.type === 'memoryChallenge' && showSequence) + ) && !showTimeoutMessage && !showFeedback && ( +
+
+
+ +
+
+
+ )} + + {/* Timeout Message - Show when time is up, keep space when hidden during feedback */} + {timeRemaining === 0 && ( +
+
+
+ +
+ {selectedLanguage === 'te' ? 'ముగిసింది!' : + selectedLanguage === 'mr' ? 'संपला!' : + selectedLanguage === 'kn' ? 'ಮುಗಿಯಿತು!' : + "Time Up!"} +
+
+
+
+ )} + + {/* Game Area */} +
+ {/* Game Type Indicator - Hide during Memory Challenge input phase */} + {!(currentQuestion.type === 'memoryChallenge' && !showSequence) && ( +
+
+ {currentQuestion.type === 'letterHunt' && } + {currentQuestion.type === 'quickSight' && } + {currentQuestion.type === 'memoryChallenge' && } + + {currentQuestion.type === 'letterHunt' && 'Letter Hunt'} + {currentQuestion.type === 'quickSight' && 'Quick Sight'} + {currentQuestion.type === 'memoryChallenge' && 'Memory Challenge'} + +
+
+ )} + + {/* Game-specific content via shared cores */} + {currentQuestion.type === 'letterHunt' && ( + handleAnswer(ans)} + onContinue={handleContinue} + showSpeaker={true} + showContinueButton={true} + showProgress={false} + isPreview={false} + disabled={showFeedback} + className="bg-transparent shadow-none border-0 p-0" + useContainer="none" + /> + )} + + {currentQuestion.type === 'quickSight' && ( + handleAnswer(pos)} + onContinue={handleContinue} + className="" + /> + )} + + {currentQuestion.type === 'memoryChallenge' && ( + + )} + + {/* Feedback and continue are rendered by core components */} +
+
+
+
+ ); +} + +export default CombinedLetterGames; \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/CombinedLetterGamesPreview.tsx b/src/lib/axl-explorations/src/components/games/CombinedLetterGamesPreview.tsx new file mode 100644 index 00000000..e8c22a8c --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CombinedLetterGamesPreview.tsx @@ -0,0 +1,1452 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Volume2, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw, Eye, Timer, Brain, ChevronLeft, ChevronRight } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { GameIntroduction } from "../GameIntroduction"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio, attachSlowLoadToast } from "../../utils/audioUtils"; +import { LetterHuntGameCore } from "./LetterHuntGameCore"; +import { ROARRapidVisualGameCore } from "./ROARRapidVisualGameCore"; +import { MemoryGameCore } from "./MemoryGameCore"; + +interface CombinedLetterGamesPreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = + | 'introduction' // Show game introduction + | 'countdown' // Show countdown timer + | 'demo' // Show actual demo + | 'completion'; // Show completion page with buttons + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForSpeaker' // Show speaker icon, wait for user click (Letter Hunt) + | 'waitForReady' // Show "I'm Ready" button, wait for user click (Quick Sight & Memory) + | 'showTarget' // Show target letter with timer (Quick Sight) + | 'showSequence' // Show sequence for a few seconds (Memory) + | 'instruction2' // After action clicked, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show options, wait for user to select + | 'waitForInput' // Show input area for sequence building (Memory) + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +type GameType = 'letterHunt' | 'quickSight' | 'memoryChallenge'; + +const gameInstructions = { + en: { + title: "Letter Games", + description: "Experience three exciting letter games in one adventure!", + games: { + letterHunt: { + title: "Letter Hunt", + steps: [ + "🔊 Click the speaker to hear the letter sound", + "👀 Look at all the letter options", + "🎯 Click on the correct letter", + "✨ Get points for correct answers!" + ], + instruction1: "Click the speaker icon to hear the letter sound", + instruction2: "Listen carefully to the letter sound", + instruction3: "Now, click on the matching letter from the options below", + instruction4: "Great job! You've completed the Letter Hunt demo!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Click the speaker icon to hear the letter sound", + narration2: "Listen carefully to the letter sound", + narration3: "Now, click on the matching letter from the options below", + narration4: "Great job! You've completed the Letter Hunt demo!", + demo: { + audio: "A", + options: ["A", "B", "C", "D"], + correctAnswer: "A", + explanation: "The sound 'A' matches the letter A!" + } + }, + quickSight: { + title: "Quick Sight", + steps: [ + "👁️ Look at the target letter carefully", + "⏱️ Remember it before time runs out", + "🔍 Find the letter position in the grid", + "✨ Click the correct position!" + ], + instruction1: "Get ready! You'll see a target letter. Click 'I'm Ready' when you're prepared", + instruction2: "Good! Now remember where that letter was", + instruction3: "Now find the target letter in the grid below", + instruction4: "Excellent! You found the correct position!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + readyButton: "I'm Ready", + narration1: "Get ready! You'll see a target letter. Click I'm Ready when you're prepared", + narration2: "Good! Now remember where that letter was", + narration3: "Now find the target letter in the grid below", + narration4: "Excellent! You found the correct position!", + rememberText: "Remember this letter!", + findPositionText: "Find the Letter Position", + demo: { + target: "A", + options: ["A", "B", "C", "D", "E", "F"], + correctPosition: 0, + explanation: "The letter 'A' was at position 1!" + } + }, + memoryChallenge: { + title: "Memory Challenge", + steps: [ + "👀 Watch the letter sequence carefully", + "🧠 Remember the order of letters", + "🎯 Click letters in the correct sequence", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a sequence of letters. Click 'I'm Ready' when prepared", + instruction2: "Good! Now remember that sequence", + instruction3: "Now recreate the sequence by clicking the letters in order", + instruction4: "Perfect! You remembered the sequence correctly!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + readyButton: "I'm Ready", + checkButton: "Check Sequence", + narration1: "Get ready! You'll see a sequence of letters. Click I'm Ready when prepared", + narration2: "Good! Now remember that sequence", + narration3: "Now recreate the sequence by clicking the letters in order", + narration4: "Perfect! You remembered the sequence correctly!", + rememberText: "Watch & Remember", + clickLettersText: "Click letters below...", + whatWasSequence: "What was the sequence?", + demo: { + sequence: ["A", "B", "C"], + options: ["A", "B", "C", "D", "E", "F"], + } + } + } + }, + te: { + title: "సంయుక్త అక్షర ఆటలు", + description: "ఒక సాహసంలో మూడు ఉత్తేజకరమైన అక్షర ఆటలను అనుభవించండి!", + games: { + letterHunt: { + title: "అక్షర వేట", + steps: [ + "🔊 స్పీకర్‌ను క్లిక్ చేసి అక్షర ధ్వనిని వినండి", + "👀 అన్ని అక్షర ఎంపికలను చూడండి", + "🎯 సరైన అక్షరాన్ని క్లిక్ చేయండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "అక్షర ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + instruction2: "అక్షర ధ్వనిని జాగ్రత్తగా వినండి", + instruction3: "ఇప్పుడు, క్రింది ఎంపికల నుండి సరిపోయే అక్షరంపై క్లిక్ చేయండి", + instruction4: "బాగా చేసారు! మీరు అక్షర వేట డెమోను పూర్తి చేసారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "అక్షర ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + narration2: "అక్షర ధ్వనిని జాగ్రత్తగా వినండి", + narration3: "ఇప్పుడు, క్రింది ఎంపికల నుండి సరిపోయే అక్షరంపై క్లిక్ చేయండి", + narration4: "బాగా చేసారు! మీరు అక్షర వేట డెమోను పూర్తి చేసారు!", + demo: { + audio: "అ", + options: ["అ", "ఆ", "ఇ", "ఈ"], + correctAnswer: "అ", + explanation: "ధ్వని 'అ' అక్షరం 'అ'కు సరిపోతుంది!" + } + }, + quickSight: { + title: "ROAR రాపిడ్ విజువల్", + steps: [ + "👁️ లక్ష్య అక్షరాన్ని జాగ్రత్తగా చూడండి", + "⏱️ సమయం ముగిసే ముందు దానిని గుర్తుంచుకోండి", + "🔍 గ్రిడ్‌లో అక్షర స్థానాన్ని కనుగొనండి", + "✨ సరైన స్థానాన్ని క్లిక్ చేయండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు లక్ష్య అక్షరాన్ని చూస్తారు. మీరు సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఆ అక్షరం ఎక్కడ ఉందో గుర్తుంచుకోండి", + instruction3: "ఇప్పుడు క్రింది గ్రిడ్‌లో లక్ష్య అక్షరాన్ని కనుగొనండి", + instruction4: "అద్భుతం! మీరు సరైన స్థానాన్ని కనుగొన్నారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + narration1: "సిద్ధంగా ఉండండి! మీరు లక్ష్య అక్షరాన్ని చూస్తారు. మీరు సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఆ అక్షరం ఎక్కడ ఉందో గుర్తుంచుకోండి", + narration3: "ఇప్పుడు క్రింది గ్రిడ్‌లో లక్ష్య అక్షరాన్ని కనుగొనండి", + narration4: "అద్భుతం! మీరు సరైన స్థానాన్ని కనుగొన్నారు!", + rememberText: "ఈ అక్షరాన్ని గుర్తుంచుకోండి!", + findPositionText: "అక్షర స్థానాన్ని కనుగొనండి", + demo: { + target: "అ", + options: ["అ", "ఆ", "ఇ", "ఈ", "ఉ", "ఊ"], + correctPosition: 0, + explanation: "అక్షరం 'అ' స్థానం 1లో ఉంది!" + } + }, + memoryChallenge: { + title: "జ్ఞాపక సవాల్", + steps: [ + "🔊 స్పీకర్‌ను క్లిక్ చేసి అక్షర క్రమాన్ని వినండి", + "👀 అక్షరాలు కనిపించేటప్పుడు చూడండి", + "🎯 క్రమాన్ని గుర్తుంచుకోండి", + "✨ మీ జ్ఞాపక నైపుణ్యాలను పరీక్షించండి!" + ], + instruction1: "అక్షర క్రమాన్ని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + instruction2: "అక్షరాలు కనిపించేటప్పుడు జాగ్రత్తగా చూడండి", + instruction3: "ఇప్పుడు, సరైన క్రమంలో అక్షరాలను క్లిక్ చేయండి", + instruction4: "బాగా చేసారు! మీరు జ్ఞాపక సవాల్ డెమోను పూర్తి చేసారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "అక్షర క్రమాన్ని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + narration2: "అక్షరాలు కనిపించేటప్పుడు జాగ్రత్తగా చూడండి", + narration3: "ఇప్పుడు, సరైన క్రమంలో అక్షరాలను క్లిక్ చేయండి", + narration4: "బాగా చేసారు! మీరు జ్ఞాపక సవాల్ డెమోను పూర్తి చేసారు!", + demo: { + audio: "అ-ఆ", + sequence: ["అ", "ఆ", "ఇ"], + options: ["అ", "ఆ", "ఇ", "ఈ", "ఉ", "ఊ"], + correctAnswer: "అ", + explanation: "క్రమం 'అ-ఆ' అక్షరం 'అ'తో ప్రారంభమవుతుంది!" + } + } + } + }, + kn: { + title: "ಸಂಯೋಜಿತ ಅಕ್ಷರ ಆಟಗಳು", + description: "ಒಂದು ಸಾಹಸದಲ್ಲಿ ಮೂರು ರೋಮಾಂಚಕ ಅಕ್ಷರ ಆಟಗಳನ್ನು ಅನುಭವಿಸಿ!", + games: { + letterHunt: { + title: "ಅಕ್ಷರ ಬೇಟೆ", + steps: [ + "🔊 ಸ್ಪೀಕರ್ ಅನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಿ", + "👀 ಎಲ್ಲಾ ಅಕ್ಷರ ಆಯ್ಕೆಗಳನ್ನು ನೋಡಿ", + "🎯 ಸರಿಯಾದ ಅಕ್ಷರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಆಲಿಸಿ", + instruction3: "ಈಗ, ಕೆಳಗಿನ ಆಯ್ಕೆಗಳಿಂದ ಹೊಂದಿಕೆಯಾಗುವ ಅಕ್ಷರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಅಕ್ಷರ ಬೇಟೆ ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಆಲಿಸಿ", + narration3: "ಈಗ, ಕೆಳಗಿನ ಆಯ್ಕೆಗಳಿಂದ ಹೊಂದಿಕೆಯಾಗುವ ಅಕ್ಷರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಅಕ್ಷರ ಬೇಟೆ ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + demo: { + audio: "ಅಂ", + options: ["ಅಂ", "ಆ", "ಇ", "ಈ"], + correctAnswer: "ಅಂ", + explanation: "ಶಬ್ದ 'ಅ' ಅಕ್ಷರ 'ಅ'ಗೆ ಹೊಂದಿಕೆಯಾಗುತ್ತದೆ!" + } + }, + quickSight: { + title: "ROAR ರಾಪಿಡ್ ವಿಜುವಲ್", + steps: [ + "👁️ ಗುರಿ ಅಕ್ಷರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + "⏱️ ಸಮಯ ಮುಗಿಯುವ ಮೊದಲು ಅದನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + "🔍 ಗ್ರಿಡ್‌ನಲ್ಲಿ ಅಕ್ಷರ ಸ್ಥಾನವನ್ನು ಹುಡುಕಿ", + "✨ ಸರಿಯಾದ ಸ್ಥಾನವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ!" + ], + instruction1: "ತಯಾರಾಗಿ! ನೀವು ಗುರಿ ಅಕ್ಷರವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಆ ಅಕ್ಷರ ಎಲ್ಲಿ ಇತ್ತು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + instruction3: "ಈಗ ಕೆಳಗಿನ ಗ್ರಿಡ್‌ನಲ್ಲಿ ಗುರಿ ಅಕ್ಷರವನ್ನು ಹುಡುಕಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಸರಿಯಾದ ಸ್ಥಾನವನ್ನು ಕಂಡುಕೊಂಡಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + readyButton: "ನಾನು ಸಿದ್ಧ", + narration1: "ತಯಾರಾಗಿ! ನೀವು ಗುರಿ ಅಕ್ಷರವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಆ ಅಕ್ಷರ ಎಲ್ಲಿ ಇತ್ತು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + narration3: "ಈಗ ಕೆಳಗಿನ ಗ್ರಿಡ್‌ನಲ್ಲಿ ಗುರಿ ಅಕ್ಷರವನ್ನು ಹುಡುಕಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಸರಿಯಾದ ಸ್ಥಾನವನ್ನು ಕಂಡುಕೊಂಡಿದ್ದೀರಿ!", + rememberText: "ಈ ಅಕ್ಷರವನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ!", + findPositionText: "ಅಕ್ಷರ ಸ್ಥಾನವನ್ನು ಹುಡುಕಿ", + demo: { + target: "ಅ", + options: ["ಅ", "ಆ", "ಇ", "ಈ", "ಉ", "ಊ"], + correctPosition: 0, + explanation: "ಅಕ್ಷರ 'ಅ' ಸ್ಥಾನ 1ರಲ್ಲಿ ಇತ್ತು!" + } + }, + memoryChallenge: { + title: "ನೆನಪಿನ ಸವಾಲು", + steps: [ + "🔊 ಸ್ಪೀಕರ್ ಅನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ ಅಕ್ಷರ ಅನುಕ್ರಮವನ್ನು ಕೇಳಿ", + "👀 ಅಕ್ಷರಗಳು ಕಾಣಿಸುವಾಗ ವೀಕ್ಷಿಸಿ", + "🎯 ಅನುಕ್ರಮ ಕ್ರಮವನ್ನು ನೆನಪಿಡಿ", + "✨ ನಿಮ್ಮ ನೆನಪಿನ ಕೌಶಲ್ಯಗಳನ್ನು ಪರೀಕ್ಷಿಸಿ!" + ], + instruction1: "ಅಕ್ಷರ ಅನುಕ್ರಮವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಅಕ್ಷರಗಳು ಕಾಣಿಸುವಾಗ ಎಚ್ಚರಿಕೆಯಿಂದ ವೀಕ್ಷಿಸಿ", + instruction3: "ಈಗ, ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ನೆನಪಿನ ಸವಾಲು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಅಕ್ಷರ ಅನುಕ್ರಮವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಅಕ್ಷರಗಳು ಕಾಣಿಸುವಾಗ ಎಚ್ಚರಿಕೆಯಿಂದ ವೀಕ್ಷಿಸಿ", + narration3: "ಈಗ, ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ನೆನಪಿನ ಸವಾಲು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + demo: { + audio: "ಅಂ-ಆ", + sequence: ["ಅಂ", "ಆ", "ಇ"], + options: ["ಅಂ", "ಆ", "ಇ", "ಈ", "ಉ", "ಊ"], + correctAnswer: "ಅಂ", + explanation: "ಅನುಕ್ರಮ 'ಅ-ಆ' ಅಕ್ಷರ 'ಅ'ದೊಂದಿಗೆ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ!" + } + } + } + }, + mr: { + title: "एकत्रित अक्षर खेळ", + description: "एका साहसात तीन रोमांचक अक्षर खेळांचा अनुभव घ्या!", + games: { + letterHunt: { + title: "अक्षर शोध", + steps: [ + "🔊 स्पीकरवर क्लिक करून अक्षर आवाज ऐका", + "👀 सर्व अक्षर पर्याय पहा", + "🎯 योग्य अक्षरावर क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "अक्षर आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + instruction2: "अक्षर आवाज काळजीपूर्वक ऐका", + instruction3: "आता, खालील पर्यायांमधून जुळणारे अक्षर क्लिक करा", + instruction4: "उत्कृष्ट! तुम्ही अक्षर शोध डेमो यशस्वीरित्या पूर्ण केले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "अक्षर आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + narration2: "अक्षर आवाज काळजीपूर्वक ऐका", + narration3: "आता, खालील पर्यायांमधून जुळणारे अक्षर क्लिक करा", + narration4: "उत्कृष्ट! तुम्ही अक्षर शोध डेमो यशस्वीरित्या पूर्ण केले!", + demo: { + audio: "अ", + options: ["अ", "आ", "इ", "ई"], + correctAnswer: "अ", + explanation: "आवाज 'अ' अक्षर 'अ'शी जुळते!" + } + }, + quickSight: { + title: "ROAR रॅपिड व्हिज्युअल", + steps: [ + "👁️ लक्ष्य अक्षर काळजीपूर्वक पहा", + "⏱️ वेळ संपण्यापूर्वी ते लक्षात ठेवा", + "🔍 ग्रिडमध्ये अक्षर स्थान शोधा", + "✨ योग्य स्थानावर क्लिक करा!" + ], + instruction1: "तयार व्हा! तुम्हाला लक्ष्य अक्षर दिसेल. तयार असाल तेव्हा 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता ते अक्षर कुठे होते लक्षात ठेवा", + instruction3: "आता खालील ग्रिडमध्ये लक्ष्य अक्षर शोधा", + instruction4: "उत्कृष्ट! तुम्हाला योग्य स्थान सापडले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + readyButton: "मी तयार आहे", + narration1: "तयार व्हा! तुम्हाला लक्ष्य अक्षर दिसेल. तयार असाल तेव्हा मी तयार आहे क्लिक करा", + narration2: "चांगले! आता ते अक्षर कुठे होते लक्षात ठेवा", + narration3: "आता खालील ग्रिडमध्ये लक्ष्य अक्षर शोधा", + narration4: "उत्कृष್ಟ! तुम्ही द्रुत दृष्टी डेमो यशस्वीरित्या पूर्ण केले!", + demo: { + target: "अ", + options: ["अ", "आ", "इ", "ई", "उ", "ऊ"], + correctPosition: 0, + explanation: "अक्षर 'अ' स्थान 1 वर होते!" + }, + rememberText: "हे अक्षर लक्षात ठेवा!", + findPositionText: "अक्षर स्थान शोधा" + }, + memoryChallenge: { + title: "स्मरणशक्ती आव्हान", + steps: [ + "🔊 स्पीकरवर क्लिक करून अक्षर क्रम ऐका", + "👀 अक्षरे दिसत असताना पहा", + "🎯 क्रम क्रम लक्षात ठेवा", + "✨ तुमची स्मरणशक्ती कौशल्ये तपासा!" + ], + instruction1: "अक्षर क्रम ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + instruction2: "अक्षरे दिसत असताना काळजीपूर्वक पहा", + instruction3: "आता, योग्य क्रमाने अक्षरे क्लिक करा", + instruction4: "उत्कृष्ट! तुम्ही स्मरणशक्ती आव्हान डेमो यशस्वीरित्या पूर्ण केले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "अक्षर क्रम ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + narration2: "अक्षरे दिसत असताना काळजीपूर्वक पहा", + narration3: "आता, योग्य क्रमाने अक्षरे क्लिक करा", + narration4: "उत्कृष्ट! तुम्ही स्मरणशक्ती आव्हान डेमो यशस्वीरित्या पूर्ण केले!", + demo: { + audio: "अ-आ", + sequence: ["अ", "आ", "इ"], + options: ["अ", "आ", "इ", "ई", "उ", "ऊ"], + correctAnswer: "अ", + explanation: "क्रम 'अ-आ' अक्षर 'अ'ने सुरू होतो!" + } + } + } + } +}; + +export function CombinedLetterGamesPreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: CombinedLetterGamesPreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('introduction'); + const [currentGameIndex, setCurrentGameIndex] = useState(0); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedSpeaker, setHasClickedSpeaker] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [isTransitioning, setIsTransitioning] = useState(false); + const [showTransitionText, setShowTransitionText] = useState(false); + const [allGamesCompleted, setAllGamesCompleted] = useState(false); + const [userSequence, setUserSequence] = useState([]); + const [showTarget, setShowTarget] = useState(false); + const [showSequence, setShowSequence] = useState(false); + const [timeRemaining, setTimeRemaining] = useState(3); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [sequenceTimer, setSequenceTimer] = useState(3); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + const [nextGameName, setNextGameName] = useState(''); + + const speakerButtonRef = useRef(null); + const speakerDivRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + const games = Object.keys(instructions.games) as GameType[]; + const currentGame = games[currentGameIndex] as GameType; + const currentGameInstructions = instructions.games[currentGame]; + + // Play narration using audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + const gameName = 'Combined Letter Games'; + const subGameMap = { + 'letterHunt': 'Letter Hunt', + 'quickSight': 'Quick Sight', + 'memoryChallenge': 'Memory Challenge' + }; + const subGame = subGameMap[currentGame]; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Play audio sound from .wav files with TTS fallback + const playAudioSound = async (text: string): Promise => { + return new Promise((resolve) => { + // Determine the correct audio path based on language + let audioPath = ''; + if (contentLanguage === 'te') { + audioPath = `/audio/telugu/letter/${text}.wav`; + } else if (contentLanguage === 'kn') { + audioPath = `/audio/kannada/letter/${text}.wav`; + } else if (contentLanguage === 'mr') { + audioPath = `/audio/marathi/letter/${text}.wav`; + } else { + // Default to English for other languages + audioPath = `/audio/english/letter/${text}.wav`; + } + + const audio = new Audio(audioPath); + attachSlowLoadToast(audio); + + audio.play().then(() => { + // Wait for audio to finish playing + audio.onended = () => { + resolve(); + }; + audio.onerror = () => { + console.warn(`Audio file not found: ${audioPath}, falling back to TTS`); + // Fallback to TTS if .wav file doesn't exist + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = contentLanguage === 'te' ? 'te-IN' : + contentLanguage === 'kn' ? 'kn-IN' : + contentLanguage === 'mr' ? 'mr-IN' : + contentLanguage === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + + utterance.onend = () => { + resolve(); + }; + + speechSynthesis.speak(utterance); + }; + }).catch((error) => { + console.warn(`Audio file not found: ${audioPath}, falling back to TTS`); + // Fallback to TTS if .wav file doesn't exist + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = contentLanguage === 'te' ? 'te-IN' : + contentLanguage === 'kn' ? 'kn-IN' : + contentLanguage === 'mr' ? 'mr-IN' : + contentLanguage === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + + utterance.onend = () => { + resolve(); + }; + + speechSynthesis.speak(utterance); + }); + }); + }; + + // Success and failure sounds are now handled by audioUtils + + // Wrapper for GameIntroduction component (expects different signature) + const playIntroductionNarration = async (text: string): Promise => { + const gameName = 'Combined Letter Games'; + try { + await playAudio({ + gameName, + language: audioLanguage, + type: 'introduction' + }, text); + } catch (error) { + console.warn('Introduction audio playback failed, using TTS fallback:', error); + await playTTS(text, audioLanguage); + } + }; + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForSpeaker': + case 'waitForReady': + setCurrentStep(0); + break; + case 'showTarget': + case 'showSequence': + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + case 'waitForInput': + // For Letter Hunt, skip step 1 (instruction2) since we removed it + setCurrentStep(currentGame === 'letterHunt' ? 1 : 2); + break; + case 'instruction4': + case 'complete': + // For Letter Hunt, adjust final step since we removed instruction2 + setCurrentStep(currentGame === 'letterHunt' ? 2 : 3); + break; + } + }, [demoStep, currentGame]); + + // Handle introduction continue + const handleIntroductionContinue = () => { + setPreviewPhase('countdown'); + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Initialize demo - play instruction 1 + useEffect(() => { + if (previewPhase === 'demo' && demoStep === 'instruction1') { + playNarration(currentGameInstructions.narration1, 1); + setHasClickedSpeaker(false); + setHasClickedReady(false); + setSelectedAnswer(null); + setShowFeedback(false); + setUserSequence([]); + setShowTarget(false); + setShowSequence(false); + setTimeRemaining(3); + setIsTimerRunning(false); + setSequenceTimer(3); + } + }, [previewPhase, demoStep, currentGameInstructions.narration1, currentGameIndex]); + + // When instruction 1 narration finishes, move to appropriate wait step + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + // Add a minimum delay to ensure button shows as disabled for a moment + const timer = setTimeout(() => { + if (currentGame === 'letterHunt') { + setDemoStep('waitForSpeaker'); + setTimeout(() => { + speakerDivRef.current?.focus(); + }, 100); + } else if (currentGame === 'quickSight') { + setDemoStep('waitForReady'); + setTimeout(() => { + speakerButtonRef.current?.focus(); + }, 100); + } else if (currentGame === 'memoryChallenge') { + setDemoStep('waitForReady'); + setTimeout(() => { + speakerButtonRef.current?.focus(); + }, 100); + } + }, 3000); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration, currentGame]); + + // Timer logic for Quick Sight + useEffect(() => { + if (showTarget && isTimerRunning) { + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + setIsTimerRunning(false); + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } + }, [showTarget, isTimerRunning]); + + // When Quick Sight timer reaches 0, hide target and show instruction 2 + useEffect(() => { + if (showTarget && !isTimerRunning && timeRemaining === 0) { + const handleTimerEnd = async () => { + setShowTarget(false); + setDemoStep('instruction2'); + await playNarration(currentGameInstructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(currentGameInstructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }; + + handleTimerEnd(); + } + }, [showTarget, isTimerRunning, timeRemaining]); + + // Timer logic for Memory Challenge + useEffect(() => { + if (showSequence && sequenceTimer > 0) { + const timer = setInterval(() => { + setSequenceTimer(prev => { + if (prev <= 1) { + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } + }, [showSequence, sequenceTimer]); + + // When Memory Challenge sequence timer ends, move to instruction 2 + useEffect(() => { + if (showSequence && sequenceTimer === 0) { + const handleSequenceEnd = async () => { + setShowSequence(false); + setDemoStep('instruction2'); + await playNarration(currentGameInstructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(currentGameInstructions.narration3, 3); + + setDemoStep('waitForInput'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }; + + handleSequenceEnd(); + } + }, [showSequence, sequenceTimer]); + + // Handle speaker button click (Letter Hunt) + const handleSpeakerClick = async () => { + if (demoStep !== 'waitForSpeaker' || hasClickedSpeaker) return; + + setHasClickedSpeaker(true); + if (currentGame === 'letterHunt' && 'audio' in currentGameInstructions.demo) { + // Wait for the audio to finish playing before proceeding + await playAudioSound(currentGameInstructions.demo.audio); + } + + // Skip instruction2 for Letter Hunt - go directly to instruction3 + setDemoStep('instruction3'); + await playNarration(currentGameInstructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }; + + // Handle ready button click (Quick Sight & Memory) + const handleReadyClick = () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + + if (currentGame === 'quickSight') { + setShowTarget(true); + setDemoStep('showTarget'); + setTimeRemaining(3); + setIsTimerRunning(true); + + } else if (currentGame === 'memoryChallenge') { + setShowSequence(true); + setDemoStep('showSequence'); + setSequenceTimer(3); + } + }; + + // Handle option click + const handleOptionClick = async (option: string) => { + if (demoStep === 'waitForAnswer' && !showFeedback) { + // Letter Hunt and Quick Sight games + setSelectedAnswer(option); + setShowFeedback(true); + + let isCorrect = false; + if (currentGame === 'letterHunt' && 'correctAnswer' in currentGameInstructions.demo) { + isCorrect = option === currentGameInstructions.demo.correctAnswer; + } else if (currentGame === 'quickSight' && 'correctPosition' in currentGameInstructions.demo) { + isCorrect = option === currentGameInstructions.demo.options[currentGameInstructions.demo.correctPosition]; + } + + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(currentGameInstructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + setDemoStep('complete'); + + // Wait a moment, then move to next game or show completion page + setTimeout(() => { + if (currentGameIndex < games.length - 1) { + // Move to next game + moveToNextGame(); + } else { + // All games completed, show completion page + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + } + }, 2000); + } else { + // Incorrect answer handling + if (currentGame === 'letterHunt') { + // For letterHunt: Play failure sound first, then feedback message audio will play after + // The feedback audio is handled by LetterHuntGameCore component + // We reset feedback UI only after feedback audio completes (via onFeedbackAudioComplete callback) + await playFailureSound(audioLanguage, { exactLanguage: true }); + // Note: Feedback UI reset happens in onFeedbackAudioComplete callback (see LetterHuntGameCore usage below) + } else { + // For other games: Play failure sound and reset feedback after delay + await playFailureSound(audioLanguage, { exactLanguage: true }); + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + } + } else if (demoStep === 'waitForInput') { + // Memory Challenge - add letter to sequence (only if not complete) + if ('sequence' in currentGameInstructions.demo && userSequence.length < currentGameInstructions.demo.sequence.length) { + setUserSequence(prev => [...prev, option]); + } + } + }; + + // Handle sequence check for Memory Challenge + const handleCheckSequence = async () => { + if (currentGame !== 'memoryChallenge' || demoStep !== 'waitForInput') return; + + const isCorrect = 'sequence' in currentGameInstructions.demo && JSON.stringify(userSequence) === JSON.stringify(currentGameInstructions.demo.sequence); + setIsCorrectAnswer(isCorrect); + setShowFeedback(true); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(currentGameInstructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + setDemoStep('complete'); + + // Wait a moment, then move to next game or show completion page + setTimeout(() => { + if (currentGameIndex < games.length - 1) { + // Move to next game + moveToNextGame(); + } else { + // All games completed, show completion page + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + } + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setUserSequence([]); + }, 2000); + } + }; + + // Remove last letter from sequence + const removeLastLetter = () => { + if (userSequence.length > 0) { + setUserSequence(prev => prev.slice(0, -1)); + } + }; + + // Restart current game demo + const restartDemo = () => { + // Stop any ongoing audio/TTS before resetting state + stopAllAudio(); + setIsPlayingNarration(false); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedSpeaker(false); + setHasClickedReady(false); + setUserSequence([]); + setShowTarget(false); + setShowSequence(false); + setTimeRemaining(3); + setIsTimerRunning(false); + setSequenceTimer(3); + setCurrentStep(0); + setCompletionCount(0); + }; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + setIsPlayingNarration(false); + onStartGame(); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + setIsPlayingNarration(false); + onBack(); + }; + + // Move to next game + const moveToNextGame = () => { + if (currentGameIndex < games.length - 1) { + // Ensure audio from current game is fully stopped before switching + stopAllAudio(); + setIsPlayingNarration(false); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the next game name before showing transition + const nextGame = games[currentGameIndex + 1] as GameType; + setNextGameName(instructions.games[nextGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev + 1); + setSuccessfulRuns(0); + restartDemo(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } else { + // All games completed + setAllGamesCompleted(true); + setDemoStep('complete'); + } + }; + + // Manual navigation - Next game + const handleNextGame = () => { + if (currentGameIndex < games.length - 1 && !isTransitioning) { + // Stop any ongoing audio/TTS when user manually navigates + stopAllAudio(); + setIsPlayingNarration(false); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the next game name before showing transition + const nextGame = games[currentGameIndex + 1] as GameType; + setNextGameName(instructions.games[nextGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev + 1); + setSuccessfulRuns(0); + restartDemo(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } + }; + + // Manual navigation - Previous game + const handlePreviousGame = () => { + if (currentGameIndex > 0 && !isTransitioning) { + // Stop any ongoing audio/TTS when user manually navigates + stopAllAudio(); + setIsPlayingNarration(false); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the previous game name before showing transition + const previousGame = games[currentGameIndex - 1] as GameType; + setNextGameName(instructions.games[previousGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev - 1); + setSuccessfulRuns(0); + restartDemo(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } + }; + + // Help button click - restart all demos from introduction + const handleHelpClick = () => { + stopAllAudio(); + setIsPlayingNarration(false); + setPreviewPhase('introduction'); + setCurrentGameIndex(0); + setAllGamesCompleted(false); + setSuccessfulRuns(0); + setHasCompletedFirstCycle(false); + restartDemo(); + }; + + // Cleanup on unmount and game changes + useEffect(() => { + return () => { + stopAllAudio(); + // Stop any ongoing speech synthesis + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + }; + }, []); + + // Stop speech synthesis when game changes + useEffect(() => { + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + setIsPlayingNarration(false); + }, [currentGameIndex]); + + const showSpeaker = currentGame === 'letterHunt' && ( + demoStep === 'waitForSpeaker' || + demoStep === 'instruction1' || + demoStep === 'instruction3' || + demoStep === 'waitForAnswer' || + demoStep === 'instruction4' || + demoStep === 'complete' + ); + const showReadyButton = (currentGame === 'quickSight' || currentGame === 'memoryChallenge') && (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showTargetLetter = currentGame === 'quickSight' && (showTarget || demoStep === 'showTarget'); + const showSequenceDisplay = currentGame === 'memoryChallenge' && (showSequence || demoStep === 'showSequence'); + const showOptions = demoStep === 'waitForAnswer' || demoStep === 'waitForInput' || demoStep === 'instruction4'; + + // Render introduction phase + if (previewPhase === 'introduction') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + } + /> +
+
+ ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + // Render completion phase + if (previewPhase === 'completion') { + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + const handleReplayDemo = () => { + stopAllAudio(); + setCurrentGameIndex(0); + setSuccessfulRuns(0); + setHasCompletedFirstCycle(false); + setPreviewPhase('countdown'); + }; + + return ( + + ); + } + + // Render demo phase + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+
+

+ {contentLanguage === 'en' ? 'How to Play' : contentLanguage === 'te' ? 'ఎలా ఆడాలి' : contentLanguage === 'kn' ? 'ಹೇಗೆ ಆಡುವುದು' : 'कसे खेळायचे'} +

+
+ {currentGameInstructions.title} +
+
+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Transition Text */} + {showTransitionText && ( +
+
+
+ {contentLanguage === 'en' ? 'Next Game:' : contentLanguage === 'te' ? 'తదుపరి గేమ్:' : contentLanguage === 'kn' ? 'ಮುಂದಿನ ಆಟ:' : 'पुढील खेळ:'} +
+
+ {nextGameName} +
+
+
+ )} + + {/* Optimized Layout Structure */} +
+ {!showTransitionText && ( + <> + {currentGame === 'letterHunt' && ( + {}} + onSpeakerClick={handleSpeakerClick} + onFeedbackAudioComplete={() => { + /** + * Callback: Called when feedback audio sequence completes + * This ensures UI resets only after full feedback message audio finishes playing + * Sequence: failure sound → feedback1 ("chosen letter is") → selected letter → feedback2 ("try again") + */ + setShowFeedback(false); + setSelectedAnswer(null); + }} + showSpeaker={showSpeaker} + showContinueButton={false} + showProgress={false} + isPreview={true} + demoStep={demoStep} + hasClickedSpeaker={hasClickedSpeaker} + speakerButtonRef={speakerDivRef} + optionsRef={optionsRef} + showHandPointer={showOptions && demoStep === 'waitForAnswer' && !showFeedback} + disabled={!(demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete') || showFeedback} + className="bg-transparent shadow-none border-0 p-0 h-full" + useContainer="none" + audioLanguageOverride={audioLanguage} + /> + )} + + {currentGame === 'quickSight' && ( + showReadyButton ? ( +
+
+ +
+ + {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ ) : ( + handleOptionClick((currentGameInstructions as any).demo.options[pos])} + className="" + isPreview={true} + demoStep={demoStep} + showHandPointer={demoStep === 'waitForAnswer' && !showFeedback} + disabled={demoStep !== 'waitForAnswer' || showFeedback} + /> + ) + )} + + {currentGame === 'memoryChallenge' && ( + showReadyButton ? ( +
+
+ +
+ + {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ ) : ( + handleOptionClick(letter)} + onRemoveLast={removeLastLetter} + onCheckSequence={handleCheckSequence} + onContinue={() => {}} + className="" + isPreview={true} + demoStep={demoStep} + showHandPointer={demoStep === 'waitForInput' && !showFeedback && userSequence.length < (currentGameInstructions as any).demo.sequence.length} + disabled={demoStep !== 'waitForInput' || showFeedback} + sequenceTimer={sequenceTimer} + /> + ) + )} + + )} +
+
+
+ + {/* Bottom Section - All buttons in one row */} +
+ {/* Left Side - Skip Demo Button */} +
+ +
+ + {/* Center - Previous/Next Navigation - Truly Centered */} +
+ {/* Previous Button */} + + + {/* Game Indicators */} +
+ {games.map((gameKey, index) => { + const isActive = index === currentGameIndex; + const isCompleted = index < currentGameIndex; + + return ( +
+ ); + })} +
+ + {/* Next Button */} + +
+ + {/* Right Side - Placeholder */} +
+ +
+ +
+
+ + ); +} diff --git a/src/lib/axl-explorations/src/components/games/CombinedSentenceGames.tsx b/src/lib/axl-explorations/src/components/games/CombinedSentenceGames.tsx new file mode 100644 index 00000000..dbdadc9a --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CombinedSentenceGames.tsx @@ -0,0 +1,1191 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { CombinedSentenceGamesPreview } from "./CombinedSentenceGamesPreview"; +import { SentenceGameCore, type SentenceQuestion } from "./SentenceGameCore"; +import { FillInBlanksGameCore, type FillInBlanksQuestion } from "./FillInBlanksGameCore"; +import { TrueFalseGameCore, type TrueFalseQuestion } from "./TrueFalseGameCore"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Globe, CheckCircle, BookOpen, FileText, HelpCircle, Check, X } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { loadSentenceData, type Language, type SentenceData } from "../../utils/sentenceDataLoader"; +import fillInBlanksData from "../../data/fillInBlanksData.json"; +import { loadTrueFalseQuestions, TrueFalseQuestion as TrueFalseDataQuestion, DifficultyLevel } from "../../utils/trueFalseDataLoader"; +import { gameSessionTracker } from "../../utils/gameSessionTracker"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { sunbirdTelemetryService, createGameSessionData, type GameSessionData } from "../../utils/sunbirdTelemetryService"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language as LanguageType, LANGUAGES } from "../../constants/languages"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; + +// Language types +type GameType = 'sentenceBuilder' | 'fillInBlanks' | 'trueFalse'; + +interface CombinedQuestion { + id: string; + type: GameType; + // Sentence Builder fields + words?: string[]; + correct?: string[]; + // Fill in Blanks fields + sentence?: string; + missingWord?: string; + correctAnswer?: string; + options?: string[]; + // True/False fields + statement?: string; + isTrue?: boolean; + explanation?: string; + audio: string; + audioText: string; + language: Language; + complexity: string; +} + +interface CombinedSentenceGamesProps { + onBack: () => void; +} + +export function CombinedSentenceGames({ onBack }: CombinedSentenceGamesProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Preview state + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + // Telemetry state + const [telemetrySessionData, setTelemetrySessionData] = useState(null); + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Sentence Builder specific state + const [arrangedWords, setArrangedWords] = useState([]); + const [availableWords, setAvailableWords] = useState([]); + + // Drag and drop state + const [draggedElement, setDraggedElement] = useState<{word: string, index: number, type: 'available' | 'arranged'} | null>(null); + const [touchStartPos, setTouchStartPos] = useState<{x: number, y: number} | null>(null); + + // Removed global cross-category de-duplication to ensure consistent question counts per level + + // Language-specific level configurations + const getLanguageLevels = (language: LanguageType) => { + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `combinedSentence_${selectedLanguage}` : 'combinedSentence'; + + // Get current user and session-based level + const currentUser = sessionManager.getCurrentUser(); + const sessionGameProgress = currentUser ? gameSessionTracker.getGameProgress(currentUser.username, gameKey) : null; + const sessionCurrentLevel = sessionGameProgress ? gameSessionTracker.getCurrentLevel(currentUser.username, gameKey) : 1; + + // Use session-based level instead of old progress system + const currentLevel = selectedLevel || sessionCurrentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Get level progression percentages as specified + const getLevelProgression = (level: number) => { + if (level <= 4) { + return { + sentenceBuilder: 50, + fillInBlanks: 30, + trueFalse: 20 + }; + } else if (level <= 7) { + return { + sentenceBuilder: 30, + fillInBlanks: 30, + trueFalse: 40 + }; + } else { + return { + sentenceBuilder: 20, + fillInBlanks: 20, + trueFalse: 60 + }; + } + }; + + // Generate Sentence Builder questions using JSON data + const generateSentenceBuilderQuestions = (language: LanguageType, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Ensure only supported languages are passed (sentenceDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // Load sentence data from JSON + const sentenceData = loadSentenceData(supportedLanguage, difficultySettings.complexity); + + if (sentenceData.length === 0) { + console.warn(`No sentence data found for ${language} ${difficultySettings.complexity}`); + return []; + } + + const localUsedSentences: string[] = []; + const actualCount = Math.min(count, sentenceData.length); + + for (let i = 0; i < actualCount; i++) { + // Filter out sentences already used in this question set (allow reuse across categories) + const unusedSentences = sentenceData.filter(sentence => { + const sentenceKey = sentence.correct.join(' '); + return !localUsedSentences.includes(sentenceKey); + }); + + if (unusedSentences.length === 0) { + console.warn(`⚠️ No more sentence options available for ${language} ${difficultySettings.complexity}`); + break; + } + + // Pick a random sentence from unused sentences + const randomIndex = Math.floor(Math.random() * unusedSentences.length); + const sentence = unusedSentences[randomIndex]; + + // Track usage locally (no global de-duplication) + const sentenceKey = sentence.correct.join(' '); + localUsedSentences.push(sentenceKey); + + // Create properly shuffled words for the challenge + const shuffledWords = [...sentence.words].sort(() => Math.random() - 0.5); + + questions.push({ + id: `sb_${i}`, + type: 'sentenceBuilder', + words: shuffledWords, + correct: [...sentence.correct], + audio: sentence.correct.join(' '), + audioText: sentence.correct.join(' '), + language: supportedLanguage, + complexity: difficultySettings.complexity + }); + } + + return questions; + }; + + // Generate Fill in Blanks questions using JSON data + const generateFillInBlanksQuestions = (language: LanguageType, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Ensure only supported languages are passed (fillInBlanksData doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // Get questions from JSON data + const languageData = fillInBlanksData[supportedLanguage]; + if (!languageData) { + console.warn(`No data found for language: ${language}`); + return []; + } + + const levelKey = mapComplexityToSentenceLevel(difficultySettings.complexity); + const questionSet = languageData[levelKey as keyof typeof languageData] || languageData.basic; + + const localUsedQuestions: string[] = []; + const actualCount = Math.min(count, questionSet.length); + + for (let i = 0; i < actualCount; i++) { + // Filter out questions already used in this question set (allow reuse across categories) + const unusedQuestions = questionSet.filter(question => { + const questionKey = question.sentence; + return !localUsedQuestions.includes(questionKey); + }); + + if (unusedQuestions.length === 0) { + console.warn(`⚠️ No more fill-in-blanks options available for ${language} ${difficultySettings.complexity}`); + break; + } + + // Pick a random question from unused questions + const randomIndex = Math.floor(Math.random() * unusedQuestions.length); + const question = unusedQuestions[randomIndex]; + + // Track usage locally (no global de-duplication) + const questionKey = question.sentence; + localUsedQuestions.push(questionKey); + + questions.push({ + id: `fib_${i}`, + type: 'fillInBlanks', + sentence: question.sentence, + missingWord: question.missingWord, + correctAnswer: question.correctAnswer, + options: [...question.options].sort(() => Math.random() - 0.5), // Shuffle options + audio: question.sentence, + audioText: question.sentence, + language: supportedLanguage, + complexity: difficultySettings.complexity + }); + } + + return questions; + }; + + // Generate True/False questions using JSON data + const generateTrueFalseQuestions = (language: LanguageType, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Ensure only supported languages are passed (trueFalseDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + const trueFalseQuestions = loadTrueFalseQuestions( + supportedLanguage, + difficultySettings.complexity as DifficultyLevel, + count, + new Set() + ); + + const localUsedQuestions: string[] = []; + + for (let i = 0; i < trueFalseQuestions.length; i++) { + const question = trueFalseQuestions[i]; + + // Track usage locally (no global de-duplication) + localUsedQuestions.push(question.statement); + + questions.push({ + id: `tf_${i}`, + type: 'trueFalse', + statement: question.statement, + isTrue: question.isTrue, + explanation: question.explanation, + audio: question.statement, + audioText: question.statement, + language: supportedLanguage, + complexity: difficultySettings.complexity + }); + } + + return questions; + }; + + // Generate combined questions based on level progression + const generateCombinedQuestions = (language: LanguageType, level: number): CombinedQuestion[] => { + const progression = getLevelProgression(level); + const totalQuestions = 10; + + const sentenceBuilderCount = Math.round((progression.sentenceBuilder / 100) * totalQuestions); + const fillInBlanksCount = Math.round((progression.fillInBlanks / 100) * totalQuestions); + const trueFalseCount = totalQuestions - sentenceBuilderCount - fillInBlanksCount; + + const sentenceBuilderQuestions = generateSentenceBuilderQuestions(language, level, sentenceBuilderCount); + const fillInBlanksQuestions = generateFillInBlanksQuestions(language, level, fillInBlanksCount); + const trueFalseQuestions = generateTrueFalseQuestions(language, level, trueFalseCount); + + // Combine questions in order: Sentence Builder first, then Fill in Blanks, then True/False + const allQuestions = [...sentenceBuilderQuestions, ...fillInBlanksQuestions, ...trueFalseQuestions]; + + return allQuestions; + }; + + // Initialize game session and questions + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + // small delay to ensure reset state settles first + await new Promise(resolve => setTimeout(resolve, 100)); + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Start session tracking + if (currentUser) { + gameSessionTracker.startLevelSession( + currentUser.username, + gameKey, + 'Combined Sentence Games', + currentLevel + ); + // End any existing subsession before starting a new one + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + // Start telemetry subsession (only once per level) + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + } + + const newQuestions = generateCombinedQuestions(selectedLanguage, currentLevel); + + // Initialize telemetry session data (for backward compatibility) + if (currentUser) { + const gameSessionData = createGameSessionData( + gameKey, + 'Combined Sentence Games', + 'combinedSentence', + currentLevel, + selectedLanguage, + difficultySettings.complexity, + currentUser.username, + true + ); + setTelemetrySessionData(gameSessionData); + } + setQuestions(newQuestions); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, currentLevel, isGameComplete]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Reset game state when URL changes + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setArrangedWords([]); + setAvailableWords([]); + } + }, [selectedLevel]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time for telemetry + useEffect(() => { + if (currentQuestion) { + setQuestionStartTime(Date.now()); + } + }, [currentQuestionIndex]); + + // Initialize sentence builder state when question changes + useEffect(() => { + if (currentQuestion && currentQuestion.type === 'sentenceBuilder') { + // Shuffle words initially + const shuffled = [...(currentQuestion.words || [])].sort(() => Math.random() - 0.5); + setAvailableWords(shuffled); + setArrangedWords([]); + } + }, [currentQuestion]); + + // Enhanced audio function + const playAudio = (text: string, language: Language) => { + speechSynthesis.cancel(); + + setTimeout(() => { + const utterance = new SpeechSynthesisUtterance(text); + + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Faster, more natural speed for Telugu + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch for Telugu + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Faster, more natural speed for Marathi + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch for Marathi + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'en': + default: + utterance.lang = 'en-US'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Consistent speed for English + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + } + + // ✅ ENHANCED: Better voice selection with fallbacks + const voices = speechSynthesis.getVoices(); + let selectedVoice = null; + + // First try: Exact language match + selectedVoice = voices.find(voice => voice.lang === utterance.lang); + + // Second try: Language family match (e.g., 'te' for 'te-IN') + if (!selectedVoice && language) { + const langCode = utterance.lang.split('-')[0]; + selectedVoice = voices.find(voice => voice.lang.startsWith(langCode)); + } + + // Third try: Regional variations with better fallbacks + if (!selectedVoice && language === 'te') { + selectedVoice = voices.find(voice => + voice.lang.includes('te') || voice.lang.includes('Telugu') || + voice.lang.includes('hi-IN') || voice.lang.includes('hi') // Hindi fallback + ); + } + + if (!selectedVoice && language === 'mr') { + selectedVoice = voices.find(voice => + voice.lang.includes('mr') || voice.lang.includes('Marathi') || + voice.lang.includes('hi-IN') || voice.lang.includes('hi') // Hindi fallback + ); + } + + if (!selectedVoice && language === 'en') { + selectedVoice = voices.find(voice => + voice.lang.includes('en-US') || voice.lang.includes('en-GB') || + voice.lang.includes('en-IN') || voice.lang.includes('en') + ); + } + + // Final fallback: Use first available voice + if (!selectedVoice && voices.length > 0) { + selectedVoice = voices[0]; + } + + if (selectedVoice) { + utterance.voice = selectedVoice; + } + + speechSynthesis.speak(utterance); + }, 100); + }; + + // Handle keyboard input for arrow keys (True or False only) + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + if (showFeedback || isGameComplete || !currentQuestion || currentQuestion.type !== 'trueFalse') return; + + if (event.key === 'ArrowRight') { + handleAnswer(true); + } else if (event.key === 'ArrowLeft') { + handleAnswer(false); + } + }; + + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + }, [showFeedback, isGameComplete, currentQuestion]); + + + // Sentence Builder helper functions + const addWordToSentence = (word: string, index: number) => { + setArrangedWords([...arrangedWords, word]); + setAvailableWords(availableWords.filter((_, i) => i !== index)); + }; + + const removeWordFromSentence = (index: number) => { + const word = arrangedWords[index]; + setAvailableWords([...availableWords, word]); + setArrangedWords(arrangedWords.filter((_, i) => i !== index)); + }; + + // Drag and drop handlers + const handleDragStart = (e: React.DragEvent, word: string, index: number) => { + e.dataTransfer.setData('text/plain', JSON.stringify({ word, index })); + e.dataTransfer.effectAllowed = 'move'; + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + const data = JSON.parse(e.dataTransfer.getData('text/plain')); + const { word, index } = data; + + // Only add if the word is still in available words + if (availableWords[index] === word) { + addWordToSentence(word, index); + } + }; + + const handleWordDragStart = (e: React.DragEvent, word: string, index: number) => { + e.dataTransfer.setData('text/plain', JSON.stringify({ word, index })); + e.dataTransfer.effectAllowed = 'move'; + }; + + const handleWordDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleWordDrop = (e: React.DragEvent, targetIndex: number) => { + e.preventDefault(); + const data = JSON.parse(e.dataTransfer.getData('text/plain')); + const { word, index } = data; + + // If dropping on an existing word, replace it + if (targetIndex !== undefined) { + const newArrangedWords = [...arrangedWords]; + const removedWord = newArrangedWords[targetIndex]; + newArrangedWords[targetIndex] = word; + setArrangedWords(newArrangedWords); + + // Add the removed word back to available words + if (removedWord) { + setAvailableWords([...availableWords, removedWord]); + } + + // Remove the dragged word from available words + setAvailableWords(availableWords.filter((_, i) => i !== index)); + } + }; + + // Mobile touch handlers + const handleTouchStart = (e: React.TouchEvent, word: string, index: number, type: 'available' | 'arranged') => { + const touch = e.touches[0]; + setTouchStartPos({ x: touch.clientX, y: touch.clientY }); + setDraggedElement({ word, index, type }); + e.preventDefault(); + }; + + const handleTouchMove = (e: React.TouchEvent) => { + if (!draggedElement || !touchStartPos) return; + + const touch = e.touches[0]; + const deltaX = Math.abs(touch.clientX - touchStartPos.x); + const deltaY = Math.abs(touch.clientY - touchStartPos.y); + + // Only start drag if moved more than 10px + if (deltaX > 10 || deltaY > 10) { + e.preventDefault(); + } + }; + + const handleTouchEnd = (e: React.TouchEvent) => { + if (!draggedElement) return; + + const touch = e.changedTouches[0]; + const element = document.elementFromPoint(touch.clientX, touch.clientY); + + // Check if dropped on sentence building area + const sentenceArea = element?.closest('[data-drop-zone="sentence"]'); + if (sentenceArea && draggedElement.type === 'available') { + // Only add if the word is still in available words + if (availableWords[draggedElement.index] === draggedElement.word) { + addWordToSentence(draggedElement.word, draggedElement.index); + } + } + + // Check if dropped on another word + const wordElement = element?.closest('[data-word-index]'); + if (wordElement && draggedElement.type === 'available') { + const targetIndex = parseInt(wordElement.getAttribute('data-word-index') || '0'); + if (targetIndex !== undefined && availableWords[draggedElement.index] === draggedElement.word) { + const newArrangedWords = [...arrangedWords]; + const removedWord = newArrangedWords[targetIndex]; + newArrangedWords[targetIndex] = draggedElement.word; + setArrangedWords(newArrangedWords); + + if (removedWord) { + setAvailableWords([...availableWords, removedWord]); + } + + setAvailableWords(availableWords.filter((_, i) => i !== draggedElement.index)); + } + } + + setDraggedElement(null); + setTouchStartPos(null); + }; + + // Handle different game type answers + const handleAnswer = async (answer: string | boolean) => { + if (showFeedback) return; + + setSelectedAnswer(answer); + let correct = false; + + switch (currentQuestion.type) { + case 'sentenceBuilder': + correct = JSON.stringify(arrangedWords) === JSON.stringify(currentQuestion.correct); + break; + case 'fillInBlanks': + correct = answer === currentQuestion.correctAnswer; + break; + case 'trueFalse': + correct = answer === currentQuestion.isTrue; + break; + } + + setIsCorrect(correct); + setShowFeedback(true); + + // Prepare user answer for telemetry + let userAnswerForTelemetry: any = answer; + switch (currentQuestion.type) { + case 'sentenceBuilder': + userAnswerForTelemetry = arrangedWords.join(' '); + break; + case 'fillInBlanks': + userAnswerForTelemetry = answer; + break; + case 'trueFalse': + userAnswerForTelemetry = answer; + break; + } + + // Send telemetry ASSESS event + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + let correctAnswerForTelemetry: any = undefined; + switch (currentQuestion.type) { + case 'sentenceBuilder': + correctAnswerForTelemetry = currentQuestion.correct?.join(' '); + break; + case 'fillInBlanks': + correctAnswerForTelemetry = currentQuestion.correctAnswer; + break; + case 'trueFalse': + correctAnswerForTelemetry = currentQuestion.isTrue; + break; + } + await sessionTelemetryManager.sendAssessEvent( + currentQuestion.id, + currentQuestion.type, + userAnswerForTelemetry, + correctAnswerForTelemetry, + correct, + responseTime + ); + // Update subsession with question attempt + sessionTelemetryManager.updateSubSession(correct); + + recordAnswer(correct); + + // Store question summary for tracking assessment + const userAnswerForTracking = typeof userAnswerForTelemetry === 'boolean' ? String(userAnswerForTelemetry) : userAnswerForTelemetry; + const questionSummary: QuestionSummary = { + questionId: currentQuestion.id, + questionType: currentQuestion.type, + userAnswer: userAnswerForTracking, + correctAnswer: correctAnswerForTelemetry, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 1); + setTotalCorrect(prevTotal => prevTotal + 1); + } + + // Update session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.updateLevelSession( + currentUser.username, + gameKey, + currentLevel, + currentQuestionIndex + 1, + totalCorrect + (correct ? 1 : 0) + ); + } + + // Don't auto-advance - player must manually continue + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedAnswer(null); + setShowFeedback(false); + setArrangedWords([]); + setAvailableWords([]); + } else { + // Level complete - check if player can advance + const scorePercentage = (totalCorrect / questions.length) * 100; + const canAdvance = scorePercentage >= 80; // Minimum 80% to advance + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // End session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.endLevelSession( + currentUser.username, + gameKey, + currentLevel, + questions.length, + totalCorrect + ); + + // Send tracking assessment data to backend + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Combined Sentence Games', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: !canAdvance, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + // End telemetry subsession + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + // Session tracker handles level advancement automatically + // Check if level was advanced by comparing with previous level + if (currentUser) { + const newSessionProgress = gameSessionTracker.getGameProgress(currentUser.username, gameKey); + if (newSessionProgress && newSessionProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + } + + setLevelFailed(!canAdvance); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, gameKey]); + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setArrangedWords([]); + setAvailableWords([]); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateCombinedQuestions(selectedLanguage, currentLevel); + setQuestions(newQuestions); + } + }; + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/combined-sentence-games/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/combined-sentence-games'); + }; + + // Handle back button with telemetry + const handleBackWithTelemetry = async () => { + if (selectedLevel !== null && !showLevelSelector && !isGameComplete) { + await sessionTelemetryManager.endSubSessionWithBackButton(); + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.endLevelSession( + currentUser.username, + gameKey, + currentLevel, + currentQuestionIndex + 1, + totalCorrect + ); + } + } + onBack(); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; + const percentage = (totalCorrect / questions.length) * 100; + + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Sentence Master - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Language Expert - Great Progress!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Extract metadata (currentLevel from backend) + if (result.metadata?.currentLevel) { + setBackendCurrentLevel(result.metadata.currentLevel); + } + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview if enabled - only if backend level is 1, OR if forcePreview is true (for demo) + if (showPreview && selectedLanguage && (backendCurrentLevel === 1 || forcePreview)) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty="Easy" + estimatedTime="5-8 min" + level={1} + /> + ); + } + + // Show level selection screen + if (showLevelSelector) { + const failedLevelKey = `failedLevel_${gameKey}`; + const failedLevel = localStorage.getItem(failedLevelKey); + + const levelSelectorCurrentLevel = failedLevel ? parseInt(failedLevel) : sessionCurrentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Sentence Games" + showBadge={true} + onCollectBadge={() => {}} // Disabled - shows "Coming Soon" tooltip + badgeTooltip="Coming Soon" + gameKey={gameKey} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + if (levelFailed) { + return ( + + ); + } + + return ( + l.code === selectedLanguage)?.nativeName}`} + score={totalCorrect} + totalQuestions={questions.length} + starsEarned={calculateStars()} + newAchievements={getNewAchievements()} + onPlayAgain={resetGame} + onBackToHub={onBack} + hasNextLevel={currentLevel < languageLevels.maxLevels} + onNextLevel={() => { + const nextLevel = currentLevel + 1; + // Navigate to the next level + navigate(`/combined-sentence-games/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + // Render game interface based on question type + return ( +
+
+ {/* Header */} +
+ + +
+

+ Combined Sentence Games +

+
+ + + {selectedLevel !== null && selectedLevel !== sessionCurrentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ {/* Game Type Indicator */} +
+
+ {currentQuestion.type === 'sentenceBuilder' && } + {currentQuestion.type === 'fillInBlanks' && } + {currentQuestion.type === 'trueFalse' && } + + {currentQuestion.type === 'sentenceBuilder' && 'Sentence Builder'} + {currentQuestion.type === 'fillInBlanks' && 'Fill in the Blanks'} + {currentQuestion.type === 'trueFalse' && 'True or False'} + +
+
+ + {/* Game-specific content */} + {currentQuestion.type === 'sentenceBuilder' && ( + handleAnswer('')} + onContinue={handleContinue} + /> + )} + + {currentQuestion.type === 'fillInBlanks' && ( + setSelectedAnswer(answer)} + onCheckAnswer={() => handleAnswer(selectedAnswer)} + onContinue={handleContinue} + /> + )} + + {currentQuestion.type === 'trueFalse' && ( + setSelectedAnswer(answer)} + onCheckAnswer={(answer) => handleAnswer(answer)} + onContinue={handleContinue} + /> + )} +
+
+
+
+ ); +} + +// Helper function to map complexity to sentence difficulty +function mapComplexityToSentenceLevel(complexity: string): string { + switch (complexity.toLowerCase()) { + case 'basic': + return 'basic'; + case 'intermediate': + return 'intermediate'; + case 'advanced': + return 'advanced'; + case 'expert': + return 'expert'; + case 'master': + return 'master'; + default: + return 'basic'; + } +} diff --git a/src/lib/axl-explorations/src/components/games/CombinedSentenceGamesPreview.tsx b/src/lib/axl-explorations/src/components/games/CombinedSentenceGamesPreview.tsx new file mode 100644 index 00000000..5a48e7d7 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CombinedSentenceGamesPreview.tsx @@ -0,0 +1,1304 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Volume2, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw, Check, X, ChevronLeft, ChevronRight, FileText } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { GameIntroduction } from "../GameIntroduction"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { SentenceGameCore, type SentenceQuestion } from "./SentenceGameCore"; +import { FillInBlanksGameCore, type FillInBlanksQuestion } from "./FillInBlanksGameCore"; +import { TrueFalseGameCore, type TrueFalseQuestion } from "./TrueFalseGameCore"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; + +interface CombinedSentenceGamesPreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = + | 'introduction' // Show game introduction + | 'countdown' // Show countdown timer + | 'demo' // Show actual demo + | 'completion'; // Show completion page with buttons + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" (Sentence Builder) + | 'showWords' // Show the scrambled words after ready click (Sentence Builder) + | 'instruction2' // After action clicked, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +type GameType = 'sentenceBuilder' | 'fillInBlanks' | 'trueFalse'; + +const gameInstructions = { + en: { + title: "Sentence Games", + description: "Experience three exciting sentence games in one adventure!", + games: { + sentenceBuilder: { + title: "Sentence Builder", + steps: [ + "📝 Look at the scrambled words", + "🖱️ Click words in correct order", + "📋 Build your sentence step by step", + "✅ Complete the sentence!" + ], + instruction1: "Click 'I'm Ready' to see the scrambled words", + instruction2: "Good! Now look at these scrambled words", + instruction3: "Now click the words in the correct order to build a sentence", + instruction4: "Perfect! You built a complete sentence!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Click I'm Ready to see the scrambled words", + narration2: "Good! Now look at these scrambled words", + narration3: "Now click the words in the correct order to build a sentence", + narration4: "Perfect! You built a complete sentence!", + buildSentence: "Build your sentence!", + readyButton: "I'm Ready", + demo: { + scrambledWords: ["is", "good", "Reading"], + correctOrder: ["Reading", "is", "good"], + correctSentence: "Reading is good", + explanation: "Perfect! You arranged the words correctly!" + } + }, + fillInBlanks: { + title: "Fill in the Blanks", + steps: [ + "📝 Read the sentence carefully", + "👀 Look at the sentence with a blank", + "🎯 Choose the correct word to complete it", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a sentence with a missing word. Click 'I'm Ready' when prepared", + instruction2: "Good! Now read this sentence carefully", + instruction3: "Now, choose the correct word to complete the sentence", + instruction4: "Great job! You've completed the demo successfully!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Get ready! You'll see a sentence with a missing word. Click I'm Ready when prepared", + narration2: "Good! Now read this sentence carefully", + narration3: "Now, choose the correct word to complete the sentence", + narration4: "Great job! You've completed the demo successfully!", + readyButton: "I'm Ready", + demo: { + audio: "The sun is shining bright", + sentence: "The ____ is shining bright", + options: ["some", "sun", "son", "sum"], + correctAnswer: "sun", + explanation: "The word 'sun' completes the sentence correctly!" + } + }, + trueFalse: { + title: "True or False", + steps: [ + "📝 Read the statement carefully", + "👀 Read the statement carefully", + "🎯 Choose True or False", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a statement to evaluate. Click 'I'm Ready' when prepared", + instruction2: "Good! Now read this statement carefully", + instruction3: "Now, decide if this statement is true or false", + instruction4: "Great job! You've completed the demo successfully!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Get ready! You'll see a statement to evaluate. Click I'm Ready when prepared", + narration2: "Good! Now read this statement carefully", + narration3: "Now, decide if this statement is true or false", + narration4: "Great job! You've completed the demo successfully!", + readyButton: "I'm Ready", + demo: { + audio: "The sky is blue", + statement: "The sky is blue", + correctAnswer: true, + explanation: "Correct! The sky is indeed blue." + } + } + } + }, + te: { + title: "సంయుక్త వాక్య ఆటలు", + description: "ఒక సాహసంలో మూడు ఉత్తేజకరమైన వాక్య ఆటలను అనుభవించండి!", + games: { + sentenceBuilder: { + title: "వాక్య నిర్మాణం", + steps: [ + "📝 గందరగోళ పదాలను చూడండి", + "🖱️ సరైన క్రమంలో పదాలను క్లిక్ చేయండి", + "📋 దశలవారీగా మీ వాక్యాన్ని నిర్మించండి", + "✅ వాక్యాన్ని పూర్తి చేయండి!" + ], + instruction1: "గందరగోళ పదాలను చూడటానికి 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ గందరగోళ పదాలను చూడండి", + instruction3: "ఇప్పుడు వాక్యాన్ని నిర్మించడానికి సరైన క్రమంలో పదాలను క్లిక్ చేయండి", + instruction4: "పర్ఫెక్ట్! మీరు పూర్తి వాక్యాన్ని నిర్మించారు!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "గందరగోళ పదాలను చూడటానికి నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ గందరగోళ పదాలను చూడండి", + narration3: "ఇప్పుడు వాక్యాన్ని నిర్మించడానికి సరైన క్రమంలో పదాలను క్లిక్ చేయండి", + narration4: "పర్ఫెక్ట్! మీరు పూర్తి వాక్యాన్ని నిర్మించారు!", + buildSentence: "మీ వాక్యాన్ని నిర్మించండి!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + demo: { + scrambledWords: ["మంచిది", "చదవడం", "చాలా"], + correctOrder: ["చదవడం", "చాలా", "మంచిది"], + correctSentence: "చదవడం చాలా మంచిది", + explanation: "పరిపూర్ణం! మీరు పదాలను సరిగ్గా అమర్చారు!" + } + }, + fillInBlanks: { + title: "ఖాళీలు పూరించండి", + steps: [ + "📝 వాక్యాన్ని జాగ్రత్తగా చదవండి", + "👀 ఖాళీతో ఉన్న వాక్యాన్ని చూడండి", + "🎯 దానిని పూర్తి చేయడానికి సరైన పదాన్ని ఎంచుకోండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు తప్పిపోయిన పదంతో వాక్యాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + instruction3: "ఇప్పుడు, వాక్యాన్ని పూర్తి చేయడానికి సరైన పదాన్ని ఎంచుకోండి", + instruction4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "సిద్ధంగా ఉండండి! మీరు తప్పిపోయిన పదంతో వాక్యాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + narration3: "ఇప్పుడు, వాక్యాన్ని పూర్తి చేయడానికి సరైన పదాన్ని ఎంచుకోండి", + narration4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + demo: { + audio: "సూర్యుడు ప్రకాశవంతంగా వెలుగుతోంది", + sentence: "____ ప్రకాశవంతంగా వెలుగుతోంది", + options: ["కొన్ని", "సూర్యుడు", "కుమారుడు", "మొత్తం"], + correctAnswer: "సూర్యుడు", + explanation: "పదం 'సూర్యుడు' వాక్యాన్ని సరిగ్గా పూర్తి చేస్తుంది!" + } + }, + trueFalse: { + title: "సత్యం లేదా అసత్యం", + steps: [ + "📝 వాక్యాన్ని జాగ్రత్తగా చదవండి", + "👀 వాక్యాన్ని జాగ్రత్తగా చదవండి", + "🎯 సత్యం లేదా అసత్యం ఎంచుకోండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు వాక్యాన్ని అంచనా వేయబోతున్నారు. సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + instruction3: "ఇప్పుడు, ఈ వాక్యం సత్యమా లేదా అసత్యమా నిర్ణయించండి", + instruction4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "సిద్ధంగా ఉండండి! మీరు వాక్యాన్ని అంచనా వేయబోతున్నారు. సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + narration3: "ఇప్పుడు, ఈ వాక్యం సత్యమా లేదా అసత్యమా నిర్ణయించండి", + narration4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + demo: { + audio: "ఆకాశం నీలం రంగులో ఉంటుంది", + statement: "ఆకాశం నీలం రంగులో ఉంటుంది", + correctAnswer: true, + explanation: "సరైనది! ఆకాశం నిజంగా నీలం రంగులో ఉంటుంది." + } + } + } + }, + kn: { + title: "ಸಂಯೋಜಿತ ವಾಕ್ಯ ಆಟಗಳು", + description: "ಒಂದು ಸಾಹಸದಲ್ಲಿ ಮೂರು ರೋಮಾಂಚಕ ವಾಕ್ಯ ಆಟಗಳನ್ನು ಅನುಭವಿಸಿ!", + games: { + sentenceBuilder: { + title: "ವಾಕ್ಯ ನಿರ್ಮಾಣ", + steps: [ + "📝 ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಿ", + "🖱️ ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಪದಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "📋 ಹಂತ ಹಂತವಾಗಿ ನಿಮ್ಮ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿ", + "✅ ವಾಕ್ಯವನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ!" + ], + instruction1: "ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಲು 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಿ", + instruction3: "ಈಗ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಲು ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಪದಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಸಂಪೂರ್ಣ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಲು ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಿ", + narration3: "ಈಗ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಲು ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಪದಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಸಂಪೂರ್ಣ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿದ್ದೀರಿ!", + buildSentence: "ನಿಮ್ಮ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿ!", + readyButton: "ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ", + demo: { + scrambledWords: ["ಒಳ್ಳೆಯದು", "ಓದುವುದು", "ತುಂಬಾ"], + correctOrder: ["ಓದುವುದು", "ತುಂಬಾ", "ಒಳ್ಳೆಯದು"], + correctSentence: "ಓದುವುದು ತುಂಬಾ ಒಳ್ಳೆಯದು", + explanation: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದಗಳನ್ನು ಸರಿಯಾಗಿ ಜೋಡಿಸಿದ್ದೀರಿ!" + } + }, + fillInBlanks: { + title: "ಖಾಲಿ ತುಂಬಿಸಿ", + steps: [ + "📝 ವಾಕ್ಯವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "👀 ಖಾಲಿಯೊಂದಿಗೆ ಇರುವ ವಾಕ್ಯವನ್ನು ನೋಡಿ", + "🎯 ಅದನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಕಾಣೆಯಾದ ಪದದೊಂದಿಗೆ ವಾಕ್ಯವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ವಾಕ್ಯವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ಈಗ, ವಾಕ್ಯವನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಕಾಣೆಯಾದ ಪದದೊಂದಿಗೆ ವಾಕ್ಯವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ವಾಕ್ಯವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ಈಗ, ವಾಕ್ಯವನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + readyButton: "ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ", + demo: { + audio: "ಸೂರ್ಯ ಪ್ರಕಾಶವಾಗಿ ಹೊಳೆಯುತ್ತಿದೆ", + sentence: "____ ಪ್ರಕಾಶವಾಗಿ ಹೊಳೆಯುತ್ತಿದೆ", + options: ["ಕೆಲವು", "ಸೂರ್ಯ", "ಮಗ", "ಮೊತ್ತ"], + correctAnswer: "ಸೂರ್ಯ", + explanation: "ಪದ 'ಸೂರ್ಯ' ವಾಕ್ಯವನ್ನು ಸರಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸುತ್ತದೆ!" + } + }, + trueFalse: { + title: "ಸತ್ಯ ಅಥವಾ ಸುಳ್ಳು", + steps: [ + "📝 ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "👀 ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "🎯 ಸತ್ಯ ಅಥವಾ ಸುಳ್ಳು ಆರಿಸಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಹೇಳಿಕೆಯನ್ನು ಮೌಲ್ಯಮಾಪನ ಮಾಡಲಿದ್ದೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ಈಗ, ಈ ಹೇಳಿಕೆ ಸತ್ಯವೇ ಅಥವಾ ಸುಳ್ಳೇ ನಿರ್ಧರಿಸಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಹೇಳಿಕೆಯನ್ನು ಮೌಲ್ಯಮಾಪನ ಮಾಡಲಿದ್ದೀರಿ. ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ಈಗ, ಈ ಹೇಳಿಕೆ ಸತ್ಯವೇ ಅಥವಾ ಸುಳ್ಳೇ ನಿರ್ಧರಿಸಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + readyButton: "ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ", + demo: { + audio: "ಆಕಾಶ ನೀಲಿ ಬಣ್ಣದಲ್ಲಿದೆ", + statement: "ಆಕಾಶ ನೀಲಿ ಬಣ್ಣದಲ್ಲಿದೆ", + correctAnswer: true, + explanation: "ಸರಿ! ಆಕಾಶ ನಿಜವಾಗಿಯೂ ನೀಲಿ ಬಣ್ಣದಲ್ಲಿದೆ." + } + } + } + }, + mr: { + title: "एकत्रित वाक्य खेळ", + description: "एका साहसात तीन रोमांचक वाक्य खेळांचा अनुभव घ्या!", + games: { + sentenceBuilder: { + title: "वाक्य बांधकाम", + steps: [ + "📝 गोंधळलेले शब्द पहा", + "🖱️ योग्य क्रमाने शब्दांवर क्लिक करा", + "📋 टप्प्याटप्प्याने तुमचे वाक्य तयार करा", + "✅ वाक्य पूर्ण करा!" + ], + instruction1: "गोंधळलेले शब्द पाहण्यासाठी 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता ही गोंधळलेली शब्दे पहा", + instruction3: "आता वाक्य तयार करण्यासाठी योग्य क्रमाने शब्दांवर क्लिक करा", + instruction4: "उत्कृष्ट! तुम्ही पूर्ण वाक्य तयार केले!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "गोंधळलेले शब्द पाहण्यासाठी मी तयार आहे क्लिक करा", + narration2: "चांगले! आता ही गोंधळलेली शब्दे पहा", + narration3: "आता वाक्य तयार करण्यासाठी योग्य क्रमाने शब्दांवर क्लिक करा", + narration4: "उत्कृष्ट! तुम्ही पूर्ण वाक्य तयार केले!", + buildSentence: "तुमचे वाक्य तयार करा!", + readyButton: "मी तयार आहे", + demo: { + scrambledWords: ["चांगले", "वाचन", "खूप"], + correctOrder: ["वाचन", "खूप", "चांगले"], + correctSentence: "वाचन खूप चांगले", + explanation: "परिपूर्ण! तुम्ही शब्दांची योग्य मांडणी केली!" + } + }, + fillInBlanks: { + title: "रिक्त जागा भरा", + steps: [ + "📝 वाक्य काळजीपूर्वक वाचा", + "👀 रिक्त जागेसह वाक्य पहा", + "🎯 ते पूर्ण करण्यासाठी योग्य शब्द निवडा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "तयार रहा! तुम्ही गहाळ शब्दासह वाक्य पाहाल. तयार असताना 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता हे वाक्य काळजीपूर्वक वाचा", + instruction3: "आता, वाक्य पूर्ण करण्यासाठी योग्य शब्द निवडा", + instruction4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "तयार रहा! तुम्ही गहाळ शब्दासह वाक्य पाहाल. तयार असताना मी तयार आहे क्लिक करा", + narration2: "चांगले! आता हे वाक्य काळजीपूर्वक वाचा", + narration3: "आता, वाक्य पूर्ण करण्यासाठी योग्य शब्द निवडा", + narration4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + readyButton: "मी तयार आहे", + demo: { + audio: "सूर्य तेजस्वीपणे चमकत आहे", + sentence: "____ तेजस्वीपणे चमकत आहे", + options: ["काही", "सूर्य", "मुलगा", "बेरीज"], + correctAnswer: "सूर्य", + explanation: "शब्द 'सूर्य' वाक्य योग्यरित्या पूर्ण करतो!" + } + }, + trueFalse: { + title: "खरे किंवा खोटे", + steps: [ + "📝 विधान काळजीपूर्वक वाचा", + "👀 विधान काळजीपूर्वक वाचा", + "🎯 खरे किंवा खोटे निवडा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "तयार रहा! तुम्ही विधानाचे मूल्यमापन करणार आहात. तयार असताना 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता हे विधान काळजीपूर्वक वाचा", + instruction3: "आता, हे विधान खरे आहे की खोटे ठरवा", + instruction4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "तयार रहा! तुम्ही विधानाचे मूल्यमापन करणार आहात. तयार असताना मी तयार आहे क्लिक करा", + narration2: "चांगले! आता हे विधान काळजीपूर्वक वाचा", + narration3: "आता, हे विधान खरे आहे की खोटे ठरवा", + narration4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + readyButton: "मी तयार आहे", + demo: { + audio: "आकाश निळ्या रंगाचे आहे", + statement: "आकाश निळ्या रंगाचे आहे", + correctAnswer: true, + explanation: "बरोबर! आकाश खरोखर निळ्या रंगाचे आहे." + } + } + } + } +}; + +const games: GameType[] = ['sentenceBuilder', 'fillInBlanks', 'trueFalse']; + +export function CombinedSentenceGamesPreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: CombinedSentenceGamesPreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('introduction'); + const [currentGameIndex, setCurrentGameIndex] = useState(0); + const [demoStep, setDemoStep] = useState('instruction1'); + const [currentStep, setCurrentStep] = useState(0); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + const [nextGameName, setNextGameName] = useState(''); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [showTransitionText, setShowTransitionText] = useState(false); + const [allGamesCompleted, setAllGamesCompleted] = useState(false); + const [isTransitioning, setIsTransitioning] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [selectedWords, setSelectedWords] = useState([]); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [selectedBooleanAnswer, setSelectedBooleanAnswer] = useState(null); + const [showWords, setShowWords] = useState(false); + + const readyButtonRef = useRef(null); + const optionsRef = useRef(null); + + const currentGame = games[currentGameIndex]; + const audioLanguage = (selectedAudioLanguage || 'en') as keyof typeof gameInstructions; + const contentLanguage = (selectedLanguage || 'en') as keyof typeof gameInstructions; + const instructions = gameInstructions[contentLanguage]; + const currentGameInstructions = instructions.games[currentGame]; + + // Handle introduction continue + const handleIntroductionContinue = () => { + setPreviewPhase('countdown'); + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Wrapper for GameIntroduction component (expects different signature) + const playIntroductionNarration = async (text: string): Promise => { + const gameName = 'Combined Sentence Games'; + try { + await playAudio({ + gameName, + language: audioLanguage, + type: 'introduction' + }, text); + } catch (error) { + console.warn('Introduction audio playback failed, using TTS fallback:', error); + await playTTS(text, audioLanguage); + } + }; + + // Play narration using audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + setIsPlayingNarration(true); + + const gameName = 'Combined Sentence Games'; + const subGameMap = { + 'sentenceBuilder': 'Sentence Builder', + 'fillInBlanks': 'Fill in Blanks', + 'trueFalse': 'True or False' + }; + const subGame = subGameMap[currentGame]; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Play audio sound + const playAudioSound = (text: string) => { + const utterance = new SpeechSynthesisUtterance(text); + const langForTTS = audioLanguage as Language; + utterance.lang = langForTTS === 'te' ? 'te-IN' : + langForTTS === 'kn' ? 'kn-IN' : + langForTTS === 'mr' ? 'mr-IN' : + langForTTS === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } + }; + + + // Reset feedback states when game changes + useEffect(() => { + setShowFeedback(false); + setIsCorrectAnswer(false); + }, [currentGameIndex]); + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + case 'showWords': + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (previewPhase === 'demo' && demoStep === 'instruction1') { + playNarration(currentGameInstructions.narration1, 1); + setSelectedWords([]); + setSelectedAnswer(null); + setSelectedBooleanAnswer(null); + setShowFeedback(false); + setShowWords(false); + } + }, [previewPhase, demoStep, currentGameInstructions.narration1, currentGameIndex]); + + // When instruction 1 narration finishes, move to appropriate wait step + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration, currentGame]); + + + // Handle ready button click (All games) + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + + // Show content based on game type + if (currentGame === 'sentenceBuilder') { + setShowWords(true); + setDemoStep('showWords'); + } else { + setDemoStep('instruction2'); + } + + // Wait a moment then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(currentGameInstructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(currentGameInstructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle word click (Sentence Builder) + const handleWordClick = async (word: string) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + if (currentGame !== 'sentenceBuilder') return; + + const newSelectedWords = [...selectedWords, word]; + setSelectedWords(newSelectedWords); + + if ('correctOrder' in currentGameInstructions.demo && newSelectedWords.length === currentGameInstructions.demo.correctOrder.length) { + setShowFeedback(true); + + const isCorrect = JSON.stringify(newSelectedWords) === JSON.stringify(currentGameInstructions.demo.correctOrder); + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(currentGameInstructions.narration4, 4); + + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + setDemoStep('complete'); + + // Wait a moment, then move to next game or restart cycle + setTimeout(() => { + if (currentGameIndex < games.length - 1) { + // Move to next game + moveToNextGame(); + } else { + // All games completed, show completion page + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + } + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedWords([]); + }, 2000); + } + } + }; + + // Handle option click (Fill in Blanks) + const handleOptionClick = async (option: string) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + if (currentGame !== 'fillInBlanks') return; + + setSelectedAnswer(option); + setShowFeedback(true); + + const isCorrect = 'correctAnswer' in currentGameInstructions.demo && option === currentGameInstructions.demo.correctAnswer; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(currentGameInstructions.narration4, 4); + + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + setDemoStep('complete'); + + // Wait a moment, then move to next game or show completion page + setTimeout(() => { + if (currentGameIndex < games.length - 1) { + // Move to next game + moveToNextGame(); + } else { + // All games completed, show completion page + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + } + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + }; + + // Handle true/false click + const handleBooleanClick = async (option: boolean) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + if (currentGame !== 'trueFalse') return; + + setSelectedBooleanAnswer(option); + setShowFeedback(true); + + const isCorrect = 'correctAnswer' in currentGameInstructions.demo && option === currentGameInstructions.demo.correctAnswer; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(currentGameInstructions.narration4, 4); + + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + setDemoStep('complete'); + + // Wait a moment, then move to next game or show completion page + setTimeout(() => { + if (currentGameIndex < games.length - 1) { + // Move to next game + moveToNextGame(); + } else { + // All games completed, show completion page + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + } + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedBooleanAnswer(null); + }, 2000); + } + }; + + // Restart current game demo + const restartCurrentGame = () => { + // Stop any ongoing audio/TTS before resetting state + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + setDemoStep('instruction1'); + setCurrentStep(0); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setSelectedWords([]); + setSelectedAnswer(null); + setSelectedBooleanAnswer(null); + setShowWords(false); + }; + + // Restart current game demo + const restartDemo = () => { + // Stop any ongoing audio/TTS before resetting state + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + setDemoStep('instruction1'); + setCurrentStep(0); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setSelectedWords([]); + setSelectedAnswer(null); + setSelectedBooleanAnswer(null); + setShowWords(false); + setCompletionCount(0); + }; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Move to next game + const moveToNextGame = () => { + if (currentGameIndex < games.length - 1) { + // Ensure audio from current game is fully stopped before switching + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the next game name before showing transition + const nextGame = games[currentGameIndex + 1] as GameType; + setNextGameName(instructions.games[nextGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev + 1); + restartDemo(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } else { + // All games completed + setAllGamesCompleted(true); + setDemoStep('complete'); + } + }; + + // Help button click - restart all demos from introduction + const handleHelpClick = () => { + stopAllAudio(); + setPreviewPhase('introduction'); + setCurrentGameIndex(0); + setAllGamesCompleted(false); + setSuccessfulRuns(0); + setHasCompletedFirstCycle(false); + restartCurrentGame(); + }; + + // Manual navigation - Next game + const handleNextGame = () => { + if (currentGameIndex < games.length - 1 && !isTransitioning) { + // Stop any ongoing audio/TTS when user manually navigates + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the next game name before showing transition + const nextGame = games[currentGameIndex + 1] as GameType; + setNextGameName(instructions.games[nextGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev + 1); + setSuccessfulRuns(0); + restartCurrentGame(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } + }; + + // Manual navigation - Previous game + const handlePreviousGame = () => { + if (currentGameIndex > 0 && !isTransitioning) { + // Stop any ongoing audio/TTS when user manually navigates + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the previous game name before showing transition + const previousGame = games[currentGameIndex - 1] as GameType; + setNextGameName(instructions.games[previousGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev - 1); + setSuccessfulRuns(0); + restartCurrentGame(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showOptions = demoStep === 'waitForAnswer' || demoStep === 'instruction4'; + const showWordsDisplay = currentGame === 'sentenceBuilder' && showWords && (demoStep === 'showWords' || demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions; + const showSentenceDisplay = currentGame === 'fillInBlanks' && (demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions; + const showStatementDisplay = currentGame === 'trueFalse' && (demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions; + + // Render introduction phase + if (previewPhase === 'introduction') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + } + /> +
+
+ ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + // Render completion phase + if (previewPhase === 'completion') { + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + const handleReplayDemo = () => { + stopAllAudio(); + setCurrentGameIndex(0); + setSuccessfulRuns(0); + setHasCompletedFirstCycle(false); + setPreviewPhase('countdown'); + }; + + return ( + + ); + } + + // Render demo phase + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ + {allGamesCompleted ? ( + + ) : ( +
+ )} +
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+
+

+ {contentLanguage === 'en' ? 'How to Play' : contentLanguage === 'te' ? 'ఎలా ఆడాలి' : contentLanguage === 'kn' ? 'ಹೇಗೆ ಆಡುವುದು' : 'कसे खेळायचे'} +

+
+ {currentGameInstructions.title} +
+
+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Transition Text */} + {showTransitionText && ( +
+
+
+ {contentLanguage === 'en' ? 'Next Game:' : contentLanguage === 'te' ? 'తదుపరి గేమ్:' : contentLanguage === 'kn' ? 'ಮುಂದಿನ ಆಟ:' : 'पुढील खेळ:'} +
+
+ {nextGameName} +
+
+
+ )} + + {/* Main Content Container */} +
+ {/* Ready Button (All games) - Always visible but disabled initially */} + {showReadyButton && !showTransitionText && ( +
+
+ {'readyButton' in currentGameInstructions ? currentGameInstructions.readyButton : (contentLanguage === 'en' ? "I'm Ready" : contentLanguage === 'te' ? 'నేను సిద్ధంగా ఉన్నాను' : contentLanguage === 'kn' ? 'ನಾನು ಸಿద్ధವಾಗಿದ್ದೇನೆ' : 'मी तयार आहे')} +
+ {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + + {/* Scrambled Words Display (Sentence Builder) */} + {showWordsDisplay && !showTransitionText && ( +
+
+
+ {'scrambledWords' in currentGameInstructions.demo && currentGameInstructions.demo.scrambledWords.map((word, index) => ( + + {word} + + ))} +
+
+
+ )} + + {/* Sentence Display (Fill in Blanks) */} + {currentGame === 'fillInBlanks' && showSentenceDisplay && !showTransitionText && ( +
+
+
+ {'sentence' in currentGameInstructions.demo ? currentGameInstructions.demo.sentence : ''} +
+
+
+ )} + + {/* Statement Display (True/False) */} + {showStatementDisplay && !showTransitionText && ( +
+
+
+ {'statement' in currentGameInstructions.demo ? currentGameInstructions.demo.statement : ''} +
+
+
+ )} + + {/* Options/Sentence Building Area */} + {showOptions && !showTransitionText && ( +
+ {/* Sentence Builder */} + {currentGame === 'sentenceBuilder' && ( + !selectedWords.includes(word)) : []} + showFeedback={showFeedback} + isCorrect={isCorrectAnswer} + isPreview={true} + demoStep={demoStep} + showHandPointer={demoStep === 'waitForAnswer' && !showFeedback} + disabled={demoStep !== 'waitForAnswer' || showFeedback} + onWordClick={(word) => handleWordClick(word)} + onRemoveWord={() => {}} + onCheckAnswer={() => {}} + onContinue={() => {}} + /> + )} + + {/* Fill in Blanks */} + {currentGame === 'fillInBlanks' && ( + handleOptionClick(answer)} + onCheckAnswer={() => {}} + onContinue={() => {}} + /> + )} + + {/* True/False */} + {currentGame === 'trueFalse' && ( + handleBooleanClick(answer)} + onCheckAnswer={() => {}} + onContinue={() => {}} + /> + )} +
+ )} +
+
+
+ + {/* Bottom Section - All buttons in one row */} +
+ {/* Skip Demo Button */} +
+ +
+ + {/* Center - Previous/Next Navigation - Truly Centered */} +
+ {/* Previous Button */} + + + {/* Game Indicators */} +
+ {games.map((gameKey, index) => { + const isActive = index === currentGameIndex; + const isCompleted = index < currentGameIndex; + + return ( +
+ ); + })} +
+ + {/* Next Button */} + +
+ + {/* Right Side - Start Game Button */} +
+ {/* Hand Pointer - appears when button is enabled */} + {hasCompletedFirstCycle && ( +
+ 👉 +
+ )} + + +
+
+ +
+
+ + ); +} + +export default CombinedSentenceGamesPreview; + diff --git a/src/lib/axl-explorations/src/components/games/CombinedWordGames.tsx b/src/lib/axl-explorations/src/components/games/CombinedWordGames.tsx new file mode 100644 index 00000000..3ca15735 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CombinedWordGames.tsx @@ -0,0 +1,1181 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { AudioButton } from "../AudioButton"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Globe, Volume2, Sparkles, BookOpen, Eye, EyeOff, Trash2 } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { wordDetectiveDataLoader, type Language as WordDetectiveLanguage, type ROARWordQuestion } from "../../utils/wordDetectiveDataLoader"; +import { soundMatchDataLoader, type ROARPhonemeQuestion } from "../../utils/soundMatchDataLoader"; +import { gameSessionTracker } from "../../utils/gameSessionTracker"; +import { sessionManager } from "../../utils/sessionManager"; +import { sunbirdTelemetryService, createGameSessionData, createQuestionResponseData, createGameEndSessionData, type GameSessionData } from "../../utils/sunbirdTelemetryService"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language } from "../../constants/languages"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { pictureWordsDataLoader } from "../../utils/pictureWordsDataLoader"; +import CombinedWordGamesPreview from "./CombinedWordGamesPreview"; +import { ROARWordGameCore } from "./ROARWordGameCore"; +import { ROARPhonemeGameCore } from "./ROARPhonemeGameCore"; +import { ROARPictureVocabGameCore } from "./ROARPictureVocabGameCore"; + +type GameType = 'wordDetective' | 'soundMatch' | 'pictureWords'; + +interface LanguageOption { + code: Language; + name: string; + nativeName: string; + flag: string; +} + +// Get languages from JSON data +const LANGUAGES: LanguageOption[] = wordDetectiveDataLoader.getLanguages(); + +interface CombinedQuestion { + id: string; + type: GameType; + // Word Detective fields + word?: string; + isReal?: boolean; + // Sound Match fields + target?: { + image: string; + word: string; + phoneme: string; + }; + options?: Array<{ + image: string; + word: string; + phoneme: string; + }>; + // Picture Words fields + pictureTarget?: { + image: string; + word: string; + category: string; + }; + pictureOptions?: Array<{ + image: string; + word: string; + category: string; + }>; + audio: string; + audioText: string; + language: Language; + complexity: string; +} + +interface CombinedWordGamesProps { + onBack: () => void; +} + +export function CombinedWordGames({ onBack }: CombinedWordGamesProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Preview state + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + // Telemetry state + const [telemetrySessionData, setTelemetrySessionData] = useState(null); + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations - All languages have 10 levels + const getLanguageLevels = (language: Language) => { + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `combinedWord_${selectedLanguage}` : 'combinedWord'; + + // Get current user and session-based level + const currentUser = sessionManager.getCurrentUser(); + const sessionGameProgress = currentUser ? gameSessionTracker.getGameProgress(currentUser.username, gameKey) : null; + const sessionCurrentLevel = sessionGameProgress ? gameSessionTracker.getCurrentLevel(currentUser.username, gameKey) : 1; + + // Use session-based level instead of old progress system + const currentLevel = selectedLevel || sessionCurrentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = selectedLanguage ? getLanguageLevels(selectedLanguage) : { maxLevels: 10, levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] }; + + // Debug logging for Marathi + if (selectedLanguage === 'mr') { + console.log('🔍 Marathi Debug:', { + selectedLanguage, + gameKey, + sessionGameProgress, + currentLevel, + languageLevels, + selectedLevel + }); + } + + // Get level progression percentages + const getLevelProgression = (level: number) => { + if (level <= 4) { + return { + pictureWords: 50, + soundMatch: 30, + wordDetective: 20 + }; + } else if (level <= 7) { + return { + pictureWords: 30, + soundMatch: 30, + wordDetective: 40 + }; + } else { + return { + pictureWords: 20, + soundMatch: 20, + wordDetective: 60 + }; + } + }; + + // Generate Word Detective questions using JSON data + const generateWordDetectiveQuestions = (language: Language, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Use JSON data loader for Word Detective + // Ensure only supported languages are passed (wordDetectiveDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const wordQuestions = wordDetectiveDataLoader.generateWordQuestions( + supportedLanguage, + level, + difficultySettings.complexity, + count, + new Set() + ); + + for (let i = 0; i < wordQuestions.length; i++) { + const question = wordQuestions[i]; + + questions.push({ + id: `wd_${i}`, + type: 'wordDetective', + word: question.word, + isReal: question.isReal, + audio: question.word, + audioText: question.word, + language, + complexity: difficultySettings.complexity + }); + } + + return questions; + }; + + // Generate Sound Match questions using JSON data with tracking (same as individual game) + const generateSoundMatchQuestions = (language: Language, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + console.log(`🎯 Sound Match Generation: Language=${language}, Level=${level}, Count=${count}`); + + // Use JSON data loader for Sound Match with tracking + const complexityLevel = soundMatchDataLoader.getComplexityForLevel(level); + console.log(`🎯 Complexity Level: ${complexityLevel}`); + + // ✅ QUESTION TRACKING: Get unused questions for this level + const unusedItems = soundMatchDataLoader.getUnusedQuestionsForLevel(supportedLanguage, complexityLevel); + console.log(`🎯 Unused Items: ${unusedItems.length} items found`); + + if (unusedItems.length === 0) { + console.error(`❌ No unused items found for ${language} ${complexityLevel}`); + return []; + } + + console.log(`🎯 Sound Match: Level ${level}, Available unused items: ${unusedItems.length}, Requested: ${count}`); + + // Generate questions using the same logic as ROARPhonemeGame + for (let i = 0; i < Math.min(count, unusedItems.length); i++) { + const target = unusedItems[i]; + console.log(`🎯 Processing target: ${target.word} (${target.phoneme})`); + + // ✅ QUESTION TRACKING: Mark this question as used + soundMatchDataLoader.markQuestionAsUsed(supportedLanguage, complexityLevel, target.word); + + // Find items with the same phoneme for correct answers (search across ALL levels) + const samePhonemeItems = soundMatchDataLoader.findItemsWithSamePhoneme( + supportedLanguage, + target.phoneme, + [target.word] + ); + console.log(`🎯 Same phoneme items: ${samePhonemeItems.length} found`); + + // Find items with different phonemes for distractors (search across ALL levels) + const differentPhonemeItems = soundMatchDataLoader.findItemsWithDifferentPhoneme( + supportedLanguage, + target.phoneme, + [target.word] + ); + console.log(`🎯 Different phoneme items: ${differentPhonemeItems.length} found`); + + // Create options array + const options = []; + const usedOptions = new Set(); + + // Add one item with the same phoneme as the correct answer (not the target) + // Filter out already used options + const unusedSamePhonemeItems = samePhonemeItems.filter(item => !usedOptions.has(item.word)); + + if (unusedSamePhonemeItems.length > 0) { + const correctAnswer = unusedSamePhonemeItems[0]; + console.log(`🎯 Sound Match: Target "${target.word}" (${target.phoneme}) -> Correct answer "${correctAnswer.word}" (${correctAnswer.phoneme})`); + options.push({ + image: correctAnswer.image, + word: correctAnswer.word, + phoneme: correctAnswer.phoneme + }); + usedOptions.add(correctAnswer.word); + // Mark the correct answer as used in tracking + soundMatchDataLoader.markQuestionAsUsed(supportedLanguage, complexityLevel, correctAnswer.word); + } else { + console.log(`⚠️ Sound Match: No unused same phoneme items found for "${target.word}" (${target.phoneme}), using target as fallback`); + // Fallback: if no same phoneme items, use the target itself + options.push({ + image: target.image, + word: target.word, + phoneme: target.phoneme + }); + usedOptions.add(target.word); + } + + // Add 3 distractors with different phonemes - filter out used options + const unusedDifferentPhonemeItems = differentPhonemeItems.filter(item => !usedOptions.has(item.word)); + const shuffledDistractors = [...unusedDifferentPhonemeItems].sort(() => Math.random() - 0.5); + + for (let j = 0; j < 3 && j < shuffledDistractors.length; j++) { + const distractor = shuffledDistractors[j]; + options.push({ + image: distractor.image, + word: distractor.word, + phoneme: distractor.phoneme + }); + usedOptions.add(distractor.word); + // Mark each distractor as used in tracking + soundMatchDataLoader.markQuestionAsUsed(supportedLanguage, complexityLevel, distractor.word); + } + + console.log(`🔍 Options selected for "${target.word}":`, options.map(opt => `${opt.word} (${opt.phoneme})`)); + + // Shuffle options so correct answer isn't always first + const shuffledOptions = options.sort(() => Math.random() - 0.5); + + console.log(`📋 Sound Match Options for "${target.word}":`, shuffledOptions.map(opt => `${opt.word} (${opt.phoneme})`)); + + questions.push({ + id: `sm_${i}`, + type: 'soundMatch', + target: { + image: target.image, + word: target.word, + phoneme: target.phoneme + }, + options: shuffledOptions, + audio: target.word, + audioText: target.word, + language, + complexity: complexityLevel + }); + } + + console.log(`🎯 Sound Match Generation Complete: Generated ${questions.length} questions`); + return questions; + }; + + // Generate Picture Words questions using child-friendly hardcoded data + const generatePictureWordsQuestions = (language: Language, level: number, count: number): CombinedQuestion[] => { + const questions: CombinedQuestion[] = []; + + // Generate picture vocabulary questions with child-friendly approach + const pictureQuestions = generateChildFriendlyPictureQuestions( + language, + level, + difficultySettings.complexity, + count, + new Set() + ); + + for (let i = 0; i < pictureQuestions.length; i++) { + const question = pictureQuestions[i]; + + questions.push({ + id: `pw_${i}`, + type: 'pictureWords', + pictureTarget: question.target, + pictureOptions: question.options, + audio: question.target.word, + audioText: question.target.word, + language, + complexity: question.complexity, + }); + } + + return questions; + }; + + // Generate combined questions based on level progression + const generateCombinedQuestions = (language: Language, level: number): CombinedQuestion[] => { + const progression = getLevelProgression(level); + const totalQuestions = 10; + + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // Reset question tracking for Sound Match to ensure fresh questions + soundMatchDataLoader.resetQuestionTracking(supportedLanguage); + console.log(`🔄 Reset Sound Match tracking for ${language}`); + + const pictureWordsCount = Math.round((progression.pictureWords / 100) * totalQuestions); + const soundMatchCount = Math.round((progression.soundMatch / 100) * totalQuestions); + const wordDetectiveCount = totalQuestions - pictureWordsCount - soundMatchCount; + + const pictureWordsQuestions = generatePictureWordsQuestions(language, level, pictureWordsCount); + const soundMatchQuestions = generateSoundMatchQuestions(language, level, soundMatchCount); + const wordDetectiveQuestions = generateWordDetectiveQuestions(language, level, wordDetectiveCount); + + // Combine questions in order: Picture Words first, then Sound Match, then Word Detective + const allQuestions = [...pictureWordsQuestions, ...soundMatchQuestions, ...wordDetectiveQuestions]; + + return allQuestions; + }; + + // Initialize game session and questions + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + + // Add a small delay to ensure state reset completes first + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(selectedLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); // Initialize for first question + setQuestionSummaries([]); + + // Start session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.startLevelSession( + currentUser.username, + gameKey, + 'Combined Word Games', + selectedLevel + ); + + // End any existing subsession before starting a new one + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + + // Start telemetry subsession (only once per level) + await sessionTelemetryManager.startSubSession(gameKey, selectedLevel, selectedLanguage); + } + + const newQuestions = generateCombinedQuestions(selectedLanguage, selectedLevel); + + // Initialize telemetry session data (for backward compatibility) + if (currentUser) { + const gameSessionData = createGameSessionData( + gameKey, + "Combined Word Games", + "combinedWord", + selectedLevel, + selectedLanguage, + difficultySettings.complexity, + currentUser.username, + true // isCombinedGame + ); + setTelemetrySessionData(gameSessionData); + } + + console.log('Generated combined word questions:', newQuestions); + setQuestions(newQuestions); + } + }; + + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Reset game state when URL changes + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + } + }, [selectedLevel]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time for telemetry + useEffect(() => { + if (currentQuestion) { + setQuestionStartTime(Date.now()); + } + }, [currentQuestionIndex]); + + // Enhanced audio function + const playAudio = (text: string, language: Language) => { + speechSynthesis.cancel(); + + setTimeout(() => { + const utterance = new SpeechSynthesisUtterance(text); + + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Faster, more natural speed for Telugu + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch for Telugu + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Faster, more natural speed for Marathi + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch for Marathi + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'kn': + utterance.lang = 'kn-IN'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Faster, more natural speed for Kannada + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch for Kannada + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + case 'en': + default: + utterance.lang = 'en-US'; + utterance.rate = 0.9; // ✅ OPTIMIZED: Consistent speed for English + utterance.pitch = 1.0; // ✅ NATURAL: Normal pitch + utterance.volume = 1.0; // ✅ CLEAR: Full volume for clarity + break; + } + + // ✅ ENHANCED: Better voice selection with fallbacks + const voices = speechSynthesis.getVoices(); + let selectedVoice = null; + + // First try: Exact language match + selectedVoice = voices.find(voice => voice.lang === utterance.lang); + + // Second try: Language family match + if (!selectedVoice && language) { + const langCode = utterance.lang.split('-')[0]; + selectedVoice = voices.find(voice => voice.lang.startsWith(langCode)); + } + + // Third try: Regional variations with better fallbacks + if (!selectedVoice && language === 'te') { + selectedVoice = voices.find(voice => + voice.lang.includes('te') || voice.lang.includes('Telugu') || + voice.lang.includes('hi-IN') || voice.lang.includes('hi') + ); + } + + if (!selectedVoice && language === 'mr') { + selectedVoice = voices.find(voice => + voice.lang.includes('mr') || voice.lang.includes('Marathi') || + voice.lang.includes('hi-IN') || voice.lang.includes('hi') + ); + } + + if (!selectedVoice && language === 'en') { + selectedVoice = voices.find(voice => + voice.lang.includes('en-US') || voice.lang.includes('en-GB') || + voice.lang.includes('en-IN') || voice.lang.includes('en') + ); + } + + // Final fallback: Use first available voice + if (!selectedVoice && voices.length > 0) { + selectedVoice = voices[0]; + } + + if (selectedVoice) { + utterance.voice = selectedVoice; + } + + speechSynthesis.speak(utterance); + }, 100); + }; + + // Auto-play audio when question changes (excluding wordDetective) + useEffect(() => { + if (currentQuestion && !showFeedback && selectedLanguage && + selectedLevel !== null && !showLevelSelector) { + const timer = setTimeout(() => { + // Play audio for soundMatch only (wordDetective audio removed) + if (currentQuestion.type === 'soundMatch') { + playAudio(currentQuestion.audioText, selectedLanguage); + } + }, 500); + + return () => clearTimeout(timer); + } + }, [currentQuestionIndex, currentQuestion, showFeedback, selectedLanguage, selectedLevel, showLevelSelector]); + + + + // Handle different game type answers + const handleAnswer = async (answer: string | boolean) => { + if (showFeedback) return; + + setSelectedAnswer(answer); + let correct = false; + + switch (currentQuestion.type) { + case 'wordDetective': + correct = answer === currentQuestion.isReal; + break; + case 'soundMatch': + const selectedOption = currentQuestion.options?.find(opt => opt.word === answer); + correct = selectedOption?.phoneme === currentQuestion.target?.phoneme; + break; + case 'pictureWords': + correct = answer === currentQuestion.pictureTarget?.word; + break; + } + + setIsCorrect(correct); + setShowFeedback(true); + + // Send telemetry ASSESS event + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + + // Determine the correct answer for telemetry based on game type + let correctAnswerForTelemetry; + switch (currentQuestion.type) { + case 'wordDetective': + // For word detective, include both the word and whether it's real + correctAnswerForTelemetry = `${currentQuestion.word}: ${currentQuestion.isReal ? 'real' : 'fake'}`; + break; + case 'soundMatch': + // For sound match, the correct answer should be the word that matches the target phoneme + const correctOption = currentQuestion.options?.find(opt => opt.phoneme === currentQuestion.target?.phoneme); + correctAnswerForTelemetry = correctOption?.word || currentQuestion.target?.word; + break; + case 'pictureWords': + correctAnswerForTelemetry = currentQuestion.pictureTarget?.word; + break; + default: + correctAnswerForTelemetry = answer; + } + + await sessionTelemetryManager.sendAssessEvent( + currentQuestion.id, + currentQuestion.type, + answer, + correctAnswerForTelemetry, + correct, + responseTime + ); + + // Update subsession with question attempt + sessionTelemetryManager.updateSubSession(correct); + recordAnswer(correct); + + // Store question summary for tracking assessment + const userAnswerForTracking = typeof answer === 'boolean' ? String(answer) : answer; + const questionSummary: QuestionSummary = { + questionId: currentQuestion.id, + questionType: currentQuestion.type, + userAnswer: userAnswerForTracking, + correctAnswer: correctAnswerForTelemetry, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 1); + setTotalCorrect(prevTotal => prevTotal + 1); + } + + // Update session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.updateLevelSession( + currentUser.username, + gameKey, + currentLevel, + currentQuestionIndex + 1, + totalCorrect + (correct ? 1 : 0) + ); + } + + // Don't auto-advance - player must manually continue + }; + + // Handle keyboard input for Word Detective game only + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + // Only work for Word Detective game and when not showing feedback + if (showFeedback || isGameComplete || !currentQuestion || currentQuestion.type !== 'wordDetective') return; + + // Left arrow = Real Word, Right arrow = Fake Word + if (event.key === 'ArrowLeft') { + handleAnswer(true); // Real Word + } else if (event.key === 'ArrowRight') { + handleAnswer(false); // Fake Word + } + }; + + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + }, [showFeedback, isGameComplete, currentQuestion, handleAnswer]); + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedAnswer(null); + setShowFeedback(false); + } else { + // Level complete - check if player can advance + const scorePercentage = (totalCorrect / questions.length) * 100; + const canAdvance = scorePercentage >= 80; // Minimum 80% to advance + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // End session tracking + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.endLevelSession( + currentUser.username, + gameKey, + currentLevel, + questions.length, + totalCorrect + ); + + // Send tracking assessment data to backend + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Combined Word Games', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: !canAdvance, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + // Session tracker handles level advancement automatically + // Check if level was advanced by comparing with previous level + if (currentUser) { + const newSessionProgress = gameSessionTracker.getGameProgress(currentUser.username, gameKey); + if (newSessionProgress && newSessionProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + } + + setLevelFailed(!canAdvance); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, gameKey, currentLevel]); + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateCombinedQuestions(selectedLanguage, currentLevel); + setQuestions(newQuestions); + } + }; + + + const handleLevelSelect = (level: number) => { + // Hide preview and navigate to the specific level URL + setShowPreview(false); + navigate(`/combined-word-games/level/${level}`); + }; + + + const handleShowLevelSelector = () => { + navigate('/combined-word-games'); + }; + + // Handle back button with telemetry + const handleBackWithTelemetry = async () => { + // Only send telemetry if we're in the middle of a level (not level selector) + if (selectedLevel !== null && !showLevelSelector && !isGameComplete) { + console.log('📊 Back button clicked during level play - sending telemetry events'); + + // End current telemetry subsession with back button context + await sessionTelemetryManager.endSubSessionWithBackButton(); + + // Update session tracking to mark level as incomplete + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + gameSessionTracker.endLevelSession( + currentUser.username, + gameKey, + currentLevel, + questions.length, + totalCorrect + ); + } + } + + // Navigate back + onBack(); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; + const percentage = (totalCorrect / questions.length) * 100; + + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Word Master - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Word Detective - Great Progress!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Extract metadata (currentLevel from backend) + if (result.metadata?.currentLevel) { + setBackendCurrentLevel(result.metadata.currentLevel); + } + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview if enabled - only if backend level is 1, OR if forcePreview is true (for demo) + if (showPreview && selectedLanguage && (backendCurrentLevel === 1 || forcePreview)) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty="Easy" + estimatedTime="5-8 min" + level={1} + /> + ); + } + + // Show level selection screen + if (showLevelSelector) { + const failedLevelKey = `failedLevel_${gameKey}`; + const failedLevel = localStorage.getItem(failedLevelKey); + + // Fix level calculation - ensure it's within bounds and has proper fallback + let levelSelectorCurrentLevel = failedLevel ? parseInt(failedLevel) : sessionCurrentLevel || 1; + + // Ensure level is within valid range (1 to maxLevels) + levelSelectorCurrentLevel = Math.max(1, Math.min(levelSelectorCurrentLevel, languageLevels.maxLevels)); + + // Debug logging for Marathi + if (selectedLanguage === 'mr') { + console.log('🔍 Marathi Level Selector Debug:', { + selectedLevel, + failedLevel, + sessionCurrentLevel: sessionCurrentLevel, + levelSelectorCurrentLevel, + languageLevels: languageLevels.maxLevels + }); + } + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Word Games" + showBadge={true} + onCollectBadge={() => {}} // Disabled - shows "Coming Soon" tooltip + badgeTooltip="Coming Soon" + gameKey={gameKey} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + if (levelFailed) { + return ( + + ); + } + + return ( + l.code === selectedLanguage)?.nativeName}`} + score={totalCorrect} + totalQuestions={questions.length} + starsEarned={calculateStars()} + newAchievements={getNewAchievements()} + onPlayAgain={resetGame} + onBackToHub={onBack} + hasNextLevel={currentLevel < languageLevels.maxLevels} + onNextLevel={() => { + const nextLevel = currentLevel + 1; + // Navigate to the next level + navigate(`/combined-word-games/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + // Render game interface based on question type + return ( +
+
+ {/* Header */} +
+ + +
+

+ Combined Word Games +

+
+ + + {selectedLevel !== null && selectedLevel !== sessionCurrentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ {/* Game Type Indicator */} +
+
+ {currentQuestion.type === 'wordDetective' && } + {currentQuestion.type === 'soundMatch' && } + {currentQuestion.type === 'pictureWords' && } + + {currentQuestion.type === 'wordDetective' && 'Word Detective'} + {currentQuestion.type === 'soundMatch' && 'Sound Match'} + {currentQuestion.type === 'pictureWords' && 'Picture Words'} + +
+
+ + {/* Game-specific content via shared cores */} + {currentQuestion.type === 'wordDetective' && ( + handleAnswer(isReal)} + onContinue={handleContinue} + className="bg-transparent shadow-none border-0 p-0" + /> + )} + + {currentQuestion.type === 'soundMatch' && ( + handleAnswer(optionWord)} + onContinue={handleContinue} + className="bg-transparent shadow-none border-0 p-0" + /> + )} + + {currentQuestion.type === 'pictureWords' && ( + handleAnswer(optionWord)} + onContinue={handleContinue} + className="bg-transparent shadow-none border-0 p-0" + /> + )} + + {/* Feedback and continue are rendered by core components */} +
+
+
+
+ ); +} +// Generate Child-Friendly Picture Vocabulary questions using JSON data +function generateChildFriendlyPictureQuestions( + language: Language, + level: number, + complexity: string, + count: number, + usedQuestions: Set +): Array<{ + target: { image: string; word: string; category: string }; + options: Array<{ image: string; word: string; category: string }>; + complexity: string; +}> { + // Use JSON data loader instead of hardcoded content + // Ensure only supported languages are passed (pictureWordsDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + return pictureWordsDataLoader.generatePictureWordQuestions( + supportedLanguage, + level, + complexity, + count, + usedQuestions + ); +} + +// Legacy function removed - now using JSON data via pictureWordsDataLoader +// File cleaned - hardcoded content removed, now uses JSON data diff --git a/src/lib/axl-explorations/src/components/games/CombinedWordGamesPreview.tsx b/src/lib/axl-explorations/src/components/games/CombinedWordGamesPreview.tsx new file mode 100644 index 00000000..be8efeff --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/CombinedWordGamesPreview.tsx @@ -0,0 +1,1315 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Volume2, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw, Eye, Timer, Brain, ChevronLeft, ChevronRight, BookOpen, Trash2 } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { GameIntroduction } from "../GameIntroduction"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio, attachSlowLoadToast } from "../../utils/audioUtils"; +import { ROARWordGameCore } from "./ROARWordGameCore"; +import { ROARPhonemeGameCore } from "./ROARPhonemeGameCore"; +import { ROARPictureVocabGameCore } from "./ROARPictureVocabGameCore"; + +interface CombinedWordGamesPreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = + | 'introduction' // Show game introduction + | 'countdown' // Show countdown timer + | 'demo' // Show actual demo + | 'completion'; // Show completion page with buttons + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForSpeaker' // Show speaker icon, wait for user click (Sound Match) + | 'waitForReady' // Show "I'm Ready" button, wait for user click (Word Detective & Picture Words) + | 'showWord' // Show word for a few seconds (Word Detective & Picture Words) + | 'showTarget' // Show target picture for Sound Match + | 'instruction2' // After action clicked, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +type GameType = 'wordDetective' | 'soundMatch' | 'pictureWords'; + +const gameInstructions = { + en: { + title: "Word Games", + description: "Experience three exciting word games in one adventure!", + games: { + wordDetective: { + title: "Word Detective", + steps: [ + "📖 Look at the word carefully", + "🤔 Think about whether it's real or fake", + "✅ Click 'Real Word' or 'Fake Word'", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a word. Click 'Show Word' when you're prepared", + instruction2: "Good! Now read this word carefully", + instruction3: "Decide: Is this a real word or a fake word?", + instruction4: "Perfect! You identified the word correctly!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Try again!", + narration1: "Get ready! You'll see a word. Click Show Word when you're prepared", + narration2: "Good! Now read this word carefully", + narration3: "Decide: Is this a real word or a fake word?", + narration4: "Perfect! You identified the word correctly!", + showWordButton: "Show Word", + demo: { + word: "CAT", + correctAnswer: "Real Word", + explanation: "CAT is a real word - it's an animal!" + } + }, + soundMatch: { + title: "Sound Match", + steps: [ + "🔊 Click the speaker to hear the sound", + "👀 Look at the picture options", + "🎯 Click the matching picture", + "✨ Get points for correct answers!" + ], + instruction1: "Click the speaker icon to hear a word sound", + instruction2: "Good! Now look at this target picture carefully", + instruction3: "Now find which picture starts with the same letter sound", + instruction4: "Perfect! You matched the sounds correctly!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Try again!", + narration1: "Click the speaker icon to hear a word sound", + narration2: "Good! Now look at this target picture carefully", + narration3: "Now find which picture starts with the same letter sound", + narration4: "Perfect! You matched the sounds correctly!", + demo: { + target: "🍞", + targetSound: "BREAD", + options: ["🍊", "🚲", "4️⃣", "🦵"], + correctIndex: 1, + explanation: "BREAD starts with 'B' and BICYCLE also starts with 'B'!" + } + }, + pictureWords: { + title: "Picture Words", + steps: [ + "👀 Look at the picture carefully", + "🤔 Think about what the word should be", + "🎯 Click the correct word", + "✨ Get points for correct answers!" + ], + instruction1: "Click 'I'm Ready' to see the word", + instruction2: "Good! Now read this word carefully", + instruction3: "Now find which picture matches this word", + instruction4: "Perfect! You matched the word with the correct picture!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Try again!", + narration1: "Click I'm Ready to see the word", + narration2: "Good! Now read this word carefully", + narration3: "Now find which picture matches this word", + narration4: "Perfect! You matched the word with the correct picture!", + readyButton: "I'm Ready", + demo: { + word: "CAT", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "The word 'CAT' matches the cat picture!" + } + } + } + }, + te: { + title: "సంయుక్త పద ఆటలు", + description: "ఒక సాహసంలో మూడు ఉత్తేజకరమైన పద ఆటలను అనుభవించండి!", + games: { + wordDetective: { + title: "పద డిటెక్టివ్", + steps: [ + "📖 పదాన్ని జాగ్రత్తగా చూడండి", + "🤔 అది నిజమైనదా లేదా కల్పితమైనదా ఆలోచించండి", + "✅ 'నిజమైన పదం' లేదా 'కల్పిత పదం' క్లిక్ చేయండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు ఒక పదాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు 'పదం చూపించు' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + instruction3: "నిర్ణయించండి: ఇది నిజమైన పదమా లేదా నకిలీ పదమా?", + instruction4: "పర్ఫెక్ట్! మీరు పదాన్ని సరిగ్గా గుర్తించారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "సిద్ధంగా ఉండండి! మీరు ఒక పదాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు పదం చూపించు క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + narration3: "నిర్ణయించండి: ఇది నిజమైన పదమా లేదా నకిలీ పదమా?", + narration4: "పర్ఫెక్ట్! మీరు పదాన్ని సరిగ్గా గుర్తించారు!", + showWordButton: "పదం చూపించు", + demo: { + word: "పిల్లి", + correctAnswer: "Real Word", + explanation: "పిల్లి ఒక నిజమైన పదం - ఇది ఒక జంతువు!" + } + }, + soundMatch: { + title: "ధ్వని మ్యాచ్", + steps: [ + "🔊 ధ్వనిని వినడానికి స్పీకర్ క్లిక్ చేయండి", + "👀 చిత్ర ఎంపికలను చూడండి", + "🎯 సరిపోయే చిత్రాన్ని క్లిక్ చేయండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "పద ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ లక్ష్య చిత్రాన్ని జాగ్రత్తగా చూడండి", + instruction3: "ఇప్పుడు ఏ చిత్రం అదే అక్షర ధ్వనితో ప్రారంభమవుతుందో కనుగొనండి", + instruction4: "పర్ఫెక్ట్! మీరు ధ్వనులను సరిగ్గా సరిపోల్చారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "పద ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ లక్ష్య చిత్రాన్ని జాగ్రత్తగా చూడండి", + narration3: "ఇప్పుడు ఏ చిత్రం అదే అక్షర ధ్వనితో ప్రారంభమవుతుందో కనుగొనండి", + narration4: "పర్ఫెక్ట్! మీరు ధ్వనులను సరిగ్గా సరిపోల్చారు!", + demo: { + target: "🐦", + targetSound: "పక్షి", + options: ["📚", "🚀", "👕", "🎈"], + correctIndex: 0, + explanation: "పక్షి 'ప' అక్షరంతో మొదలవుతుంది మరియు పుస్తకం కూడా 'ప' అక్షరంతో మొదలవుతుంది!" + } + }, + pictureWords: { + title: "చిత్ర పదాలు", + steps: [ + "👀 చిత్రాన్ని జాగ్రత్తగా చూడండి", + "🤔 పదం ఏమైనా అని ఆలోచించండి", + "🎯 సరైన పదాన్ని క్లిక్ చేయండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "పదాన్ని చూడటానికి 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + instruction3: "ఇప్పుడు ఈ పదానికి సరిపోయే చిత్రాన్ని కనుగొనండి", + instruction4: "పర్ఫెక్ట్! మీరు పదాన్ని సరైన చిత్రంతో సరిపోల్చారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "పదాన్ని చూడటానికి నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + narration3: "ఇప్పుడు ఈ పదానికి సరిపోయే చిత్రాన్ని కనుగొనండి", + narration4: "పర్ఫెక్ట్! మీరు పదాన్ని సరైన చిత్రంతో సరిపోల్చారు!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + demo: { + word: "పిల్లి", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "పదం 'పిల్లి' పిల్లి చిత్రంతో సరిపోతుంది!" + } + } + } + }, + kn: { + title: "ಸಂಯೋಜಿತ ಪದ ಆಟಗಳು", + description: "ಒಂದು ಸಾಹಸದಲ್ಲಿ ಮೂರು ರೋಮಾಂಚಕ ಪದ ಆಟಗಳನ್ನು ಅನುಭವಿಸಿ!", + games: { + wordDetective: { + title: "ಪದ ಡಿಟೆಕ್ಟಿವ್", + steps: [ + "📖 ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + "🤔 ಅದು ನಿಜವಾದದ್ದು ಅಥವಾ ನಕಲಿ ಎಂದು ಯೋಚಿಸಿ", + "✅ 'ನಿಜವಾದ ಪದ' ಅಥವಾ 'ನಕಲಿ ಪದ' ಕ್ಲಿಕ್ ಮಾಡಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ತಯಾರಾಗಿ! ನೀವು ಒಂದು ಪದವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ಪದ ತೋರಿಸಿ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ನಿರ್ಧರಿಸಿ: ಇದು ನಿಜವಾದ ಪದವೇ ಅಥವಾ ನಕಲಿ ಪದವೇ?", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾಗಿ ಗುರುತಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ತಯಾರಾಗಿ! ನೀವು ಒಂದು ಪದವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ ಪದ ತೋರಿಸಿ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ನಿರ್ಧರಿಸಿ: ಇದು ನಿಜವಾದ ಪದವೇ ಅಥವಾ ನಕಲಿ ಪದವೇ?", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾಗಿ ಗುರುತಿಸಿದ್ದೀರಿ!", + showWordButton: "ಪದ ತೋರಿಸಿ", + demo: { + word: "ಬೆಕ್ಕು", + correctAnswer: "Real Word", + explanation: "ಬೆಕ್ಕು ಒಂದು ನಿಜವಾದ ಪದ - ಅದು ಒಂದು ಪ್ರಾಣಿ!" + } + }, + soundMatch: { + title: "ಶಬ್ದ ಹೊಂದಾಣಿಕೆ", + steps: [ + "🔊 ಧ್ವನಿಯನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಕ್ಲಿಕ್ ಮಾಡಿ", + "👀 ಚಿತ್ರ ಐಚ್ಛಿಕಗಳನ್ನು ನೋಡಿ", + "🎯 ಹೊಂದಿಕೆಯಾಗುವ ಚಿತ್ರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಪದ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗುರಿ ಚಿತ್ರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + instruction3: "ಈಗ ಯಾವ ಚಿತ್ರ ಅದೇ ಅಕ್ಷರ ಶಬ್ದದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ ಎಂದು ಹುಡುಕಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಶಬ್ದಗಳನ್ನು ಸರಿಯಾಗಿ ಹೊಂದಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಪದ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗುರಿ ಚಿತ್ರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + narration3: "ಈಗ ಯಾವ ಚಿತ್ರ ಅದೇ ಅಕ್ಷರ ಶಬ್ದದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ ಎಂದು ಹುಡುಕಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಶಬ್ದಗಳನ್ನು ಸರಿಯಾಗಿ ಹೊಂದಿಸಿದ್ದೀರಿ!", + demo: { + target: "🐦", + targetSound: "ಪಕ್ಷಿ", + options: ["📚", "🚀", "👕", "🎈"], + correctIndex: 0, + explanation: "ಪಕ್ಷಿ 'ಪ' ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ ಮತ್ತು ಪುಸ್ತಕ ಕೂಡ 'ಪ' ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ!" + } + }, + pictureWords: { + title: "ಚಿತ್ರ ಪದಗಳು", + steps: [ + "👀 ಚಿತ್ರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + "🤔 ಪದವು ಏನಾಗಿರಬೇಕೆಂದು ಯೋಚಿಸಿ", + "🎯 ಸರಿಯಾದ ಪದವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಪದವನ್ನು ನೋಡಲು 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ಈಗ ಈ ಪದಕ್ಕೆ ಹೊಂದಾಣಿಕೆಯ ಚಿತ್ರವನ್ನು ಹುಡುಕಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾದ ಚಿತ್ರದೊಂದಿಗೆ ಹೊಂದಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಪದವನ್ನು ನೋಡಲು ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ಈಗ ಈ ಪದಕ್ಕೆ ಹೊಂದಾಣಿಕೆಯ ಚಿತ್ರವನ್ನು ಹುಡುಕಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾದ ಚಿತ್ರದೊಂದಿಗೆ ಹೊಂದಿಸಿದ್ದೀರಿ!", + readyButton: "ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ", + demo: { + word: "ಬೆಕ್ಕು", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "ಪದ 'ಬೆಕ್ಕು' ಬೆಕ್ಕಿನ ಚಿತ್ರದೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುತ್ತದೆ!" + } + } + } + }, + mr: { + title: "एकत्रित शब्द खेळ", + description: "एका साहसात तीन रोमांचक शब्द खेळांचा अनुभव घ्या!", + games: { + wordDetective: { + title: "शब्द डिटेक्टिव्ह", + steps: [ + "📖 शब्दाकडे काळजीपूर्वक पहा", + "🤔 तो खरा आहे की नकली याचा विचार करा", + "✅ 'खरा शब्द' किंवा 'नकली शब्द' क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "तयार व्हा! तुम्हाला एक शब्द दिसेल. तयार असाल तेव्हा 'शब्द दाखवा' क्लिक करा", + instruction2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + instruction3: "ठरवा: हा खरा शब्द आहे की नकली?", + instruction4: "परिपूर्ण! तुम्ही शब्दाची योग्य ओळख केली!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "तयार व्हा! तुम्हाला एक शब्द दिसेल. तयार असाल तेव्हा शब्द दाखवा क्लिक करा", + narration2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + narration3: "ठरवा: हा खरा शब्द आहे की नकली?", + narration4: "परिपूर्ण! तुम्ही शब्दाची योग्य ओळख केली!", + showWordButton: "शब्द दाखवा", + demo: { + word: "मांजर", + correctAnswer: "Real Word", + explanation: "मांजर हा खरा शब्द आहे - तो एक प्राणी आहे!" + } + }, + soundMatch: { + title: "आवाज जुळणी", + steps: [ + "🔊 आवाज ऐकण्यासाठी स्पीकर क्लिक करा", + "👀 चित्र पर्यायांकडे पहा", + "🎯 जुळणारे चित्र क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "शब्द आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + instruction2: "चांगले! आता हे लक्ष्य चित्र काळजीपूर्वक पहा", + instruction3: "आता कोणते चित्र त्याच अक्षराच्या आवाजाने सुरू होते ते शोधा", + instruction4: "परिपूर्ण! तुम्ही आवाजांची योग्य जुळणी केली!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "शब्द आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + narration2: "चांगले! आता हे लक्ष्य चित्र काळजीपूर्वक पहा", + narration3: "आता कोणते चित्र त्याच अक्षराच्या आवाजाने सुरू होते ते शोधा", + narration4: "परिपूर्ण! तुम्ही आवाजांची योग्य जुळणी केली!", + demo: { + target: "🍎", + targetSound: "सफरचंद", + options: ["☀️", "🚀", "👕", "📚"], + correctIndex: 0, + explanation: "सफरचंद 'स' अक्षराने सुरू होतो आणि सूर्य देखील 'स' अक्षराने सुरू होतो!" + } + }, + pictureWords: { + title: "चित्र शब्द", + steps: [ + "👀 चित्राकडे काळजीपूर्वक पहा", + "🤔 शब्द काय असावा याचा विचार करा", + "🎯 योग्य शब्द क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "शब्द पाहण्यासाठी 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + instruction3: "आता या शब्दाशी जुळणारे चित्र शोधा", + instruction4: "परिपूर्ण! तुम्ही शब्दाची योग्य चित्राशी जुळणी केली!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "शब्द पाहण्यासाठी मी तयार आहे क्लिक करा", + narration2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + narration3: "आता या शब्दाशी जुळणारे चित्र शोधा", + narration4: "परिपूर्ण! तुम्ही शब्दाची योग्य चित्राशी जुळणी केली!", + readyButton: "मी तयार आहे", + demo: { + word: "मांजर", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "शब्द 'मांजर' मांजराच्या चित्राशी जुळते!" + } + } + } + } +}; + +const games: GameType[] = ['wordDetective', 'soundMatch', 'pictureWords']; + +export function CombinedWordGamesPreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: CombinedWordGamesPreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('introduction'); + const [currentGameIndex, setCurrentGameIndex] = useState(0); + const [demoStep, setDemoStep] = useState('instruction1'); + const [currentStep, setCurrentStep] = useState(0); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + const [nextGameName, setNextGameName] = useState(''); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedSpeaker, setHasClickedSpeaker] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [showTransitionText, setShowTransitionText] = useState(false); + const [allGamesCompleted, setAllGamesCompleted] = useState(false); + const [isTransitioning, setIsTransitioning] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [showTargetState, setShowTargetState] = useState(false); + + const speakerButtonRef = useRef(null); + const optionsRef = useRef(null); + + const currentGame = games[currentGameIndex]; + const audioLanguage = (selectedAudioLanguage || 'en') as keyof typeof gameInstructions; + const contentLanguage = (selectedLanguage || 'en') as keyof typeof gameInstructions; + const instructions = gameInstructions[contentLanguage]; + const currentGameInstructions = instructions.games[currentGame]; + + // Handle introduction continue + const handleIntroductionContinue = () => { + setPreviewPhase('countdown'); + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || isPlayingNarration || hasClickedReady) return; + + setHasClickedReady(true); + + if (currentGame === 'wordDetective' || currentGame === 'pictureWords') { + setDemoStep('showWord'); + setTimeout(async () => { + setDemoStep('instruction2'); + setCurrentStep(1); + await playNarration(currentGameInstructions.narration2, 2); + + setDemoStep('instruction3'); + setCurrentStep(2); + await playNarration(currentGameInstructions.narration3, 3); + + setDemoStep('waitForAnswer'); + }, 2000); + } + }; + + const handleSpeakerClick = async () => { + if (demoStep !== 'waitForSpeaker' || hasClickedSpeaker) return; + + setHasClickedSpeaker(true); + playWordSound((currentGameInstructions.demo as any).targetSound); + + // Show target image + setShowTargetState(true); + setDemoStep('showTarget'); + + // Wait a moment then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(currentGameInstructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(currentGameInstructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + const handleOptionClick = async (option: string) => { + if (demoStep !== 'waitForAnswer' || isPlayingNarration) return; + + setShowFeedback(true); + + let isCorrect = false; + if (currentGame === 'wordDetective' && 'correctAnswer' in currentGameInstructions.demo) { + isCorrect = option === currentGameInstructions.demo.correctAnswer; + } else if (currentGame === 'soundMatch' && 'correctIndex' in currentGameInstructions.demo) { + // For soundMatch, option is the word, so check if it matches the correct word + const demoOptions = getDemoOptions(); + const correctWord = demoOptions[currentGameInstructions.demo.correctIndex]?.word; + isCorrect = option === correctWord; + } else if (currentGame === 'pictureWords' && 'correctIndex' in currentGameInstructions.demo) { + const optionIndex = currentGameInstructions.demo.options.indexOf(option); + isCorrect = optionIndex === currentGameInstructions.demo.correctIndex; + } + + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + setDemoStep('instruction4'); + setCurrentStep(3); + await playNarration(currentGameInstructions.narration4,4); + setShowFeedback(false); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + setDemoStep('complete'); + + // Wait a moment, then move to next game or show completion page + setTimeout(() => { + if (currentGameIndex < games.length - 1) { + // Move to next game + moveToNextGame(); + } else { + // All games completed, show completion page + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + } + }, 500); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + setShowFeedback(true); + setTimeout(() => { + setShowFeedback(false); + }, 2000); + } + }; + + const restartCurrentGame = () => { + // Stop any ongoing audio/TTS before resetting state + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + setDemoStep('instruction1'); + setCurrentStep(0); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedSpeaker(false); + setHasClickedReady(false); + setShowTargetState(false); + }; + + // Restart current game demo + const restartDemo = () => { + // Stop any ongoing audio/TTS before resetting state + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + setDemoStep('instruction1'); + setCurrentStep(0); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedSpeaker(false); + setHasClickedReady(false); + setShowTargetState(false); + setCompletionCount(0); + }; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Move to next game + const moveToNextGame = () => { + if (currentGameIndex < games.length - 1) { + // Ensure audio from current game is fully stopped before switching + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the next game name before showing transition + const nextGame = games[currentGameIndex + 1] as GameType; + setNextGameName(instructions.games[nextGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev + 1); + restartDemo(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } else { + // All games completed + setAllGamesCompleted(true); + setDemoStep('complete'); + } + }; + + const handleHelpClick = () => { + stopAllAudio(); + setPreviewPhase('introduction'); + restartCurrentGame(); + setAllGamesCompleted(false); + setCurrentGameIndex(0); + setSuccessfulRuns(0); + setHasCompletedFirstCycle(false); + }; + + const handleNextGame = () => { + if (currentGameIndex < games.length - 1 && !isTransitioning) { + // Stop any ongoing audio/TTS when user manually navigates + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the next game name before showing transition + const nextGame = games[currentGameIndex + 1] as GameType; + setNextGameName(instructions.games[nextGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev + 1); + setSuccessfulRuns(0); + restartCurrentGame(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } + }; + + const handlePreviousGame = () => { + if (currentGameIndex > 0 && !isTransitioning) { + // Stop any ongoing audio/TTS when user manually navigates + stopAllAudio(); + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + // Set the previous game name before showing transition + const previousGame = games[currentGameIndex - 1] as GameType; + setNextGameName(instructions.games[previousGame].title); + + setIsTransitioning(true); + setShowTransitionText(true); + + setTimeout(() => { + setCurrentGameIndex(prev => prev - 1); + setSuccessfulRuns(0); + restartCurrentGame(); + setIsTransitioning(false); + + setTimeout(() => { + setShowTransitionText(false); + setNextGameName(''); + }, 1000); + }, 1000); + } + }; + + // Wrapper for GameIntroduction component (expects different signature) + const playIntroductionNarration = async (text: string): Promise => { + const gameName = 'Combined Word Games'; + try { + await playAudio({ + gameName, + language: audioLanguage, + type: 'introduction' + }, text); + } catch (error) { + console.warn('Introduction audio playback failed, using TTS fallback:', error); + await playTTS(text, audioLanguage); + } + }; + + const playNarration = async (text: string, step: number): Promise => { + setIsPlayingNarration(true); + + const gameName = 'Combined Word Games'; + const subGameMap = { + 'wordDetective': 'Word Detective', + 'soundMatch': 'Sound Match', + 'pictureWords': 'Picture Words' + }; + const subGame = subGameMap[currentGame]; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Get demo options with proper words for soundMatch game + const getDemoOptions = () => { + const lang = contentLanguage; + const demo = (currentGameInstructions.demo as any); + + if (!demo || !demo.options || currentGame !== 'soundMatch') { + return []; + } + + if (lang === 'te') { + // Telugu: పక్షి (bird) matches with 📚 (పుస్తకం - book) + const emojiToWord: Record = { + "📚": "పుస్తకం", + "🚀": "రాకెట్", + "👕": "చొక్కా", + "🎈": "బలూన్" + }; + return demo.options.map((emoji: string, index: number) => ({ + image: emoji, + word: emojiToWord[emoji] || emoji, + phoneme: emoji === demo.options[demo.correctIndex] ? "ప" : "X" + })); + } else if (lang === 'kn') { + // Kannada: ಪಕ್ಷಿ (bird) matches with 📚 (ಪುಸ್ತಕ - book) + const emojiToWord: Record = { + "📚": "ಪುಸ್ತಕ", + "🚀": "ರಾಕೆಟ್", + "👕": "ಅಂಗಿ", + "🎈": "ಬಲೂನ್" + }; + return demo.options.map((emoji: string, index: number) => ({ + image: emoji, + word: emojiToWord[emoji] || emoji, + phoneme: emoji === demo.options[demo.correctIndex] ? "ಪ" : "X" + })); + } else if (lang === 'mr') { + // Marathi: सफरचंद (apple) matches with ☀️ (सूर्य - sun) + const emojiToWord: Record = { + "☀️": "सूर्य", + "🚀": "रॉकेट", + "👕": "शर्ट", + "📚": "पुस्तक" + }; + return demo.options.map((emoji: string, index: number) => ({ + image: emoji, + word: emojiToWord[emoji] || emoji, + phoneme: emoji === demo.options[demo.correctIndex] ? "स" : "X" + })); + } else { + // English: BREAD matches with BICYCLE + const emojiToWord: Record = { + "🍊": "ORANGE", + "🚲": "BICYCLE", + "4️⃣": "FOUR", + "🦵": "LEG" + }; + return demo.options.map((emoji: string, index: number) => ({ + image: emoji, + word: emojiToWord[emoji] || emoji, + phoneme: emoji === demo.options[demo.correctIndex] ? "B" : "X" + })); + } + }; + + // Play word sound - tries audio files first, then TTS (for sound-match game) + const playWordSound = async (text: string) => { + const language = contentLanguage as Language; + + // Only use audio files for soundMatch game, otherwise use TTS directly + if (currentGame !== 'soundMatch') { + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = language === 'te' ? 'te-IN' : + language === 'kn' ? 'kn-IN' : + language === 'mr' ? 'mr-IN' : + language === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } + return; + } + + // For soundMatch game, try to play audio file from sound-match folder first + const word = text.toLowerCase().trim(); + const audioPath = `/audio/audio-preview/combined-word-games/sound-match/${language}/${word}.wav`; + + try { + const audio = new Audio(audioPath); + attachSlowLoadToast(audio); + + // Try to play the audio file + await new Promise((resolve, reject) => { + audio.onloadeddata = () => { + audio.play().then(() => { + audio.onended = () => resolve(); + }).catch(() => { + // If playback fails, fall through to TTS + reject(); + }); + }; + + audio.onerror = () => { + // If file doesn't exist or fails to load, fall through to TTS + reject(); + }; + + // Set a timeout to prevent hanging + setTimeout(() => { + if (!audio.ended && audio.readyState < 2) { + reject(); + } + }, 2000); + }); + + // Successfully played audio file + return; + } catch (error) { + // Fall back to TTS if audio file doesn't exist or fails + console.warn(`Audio file not found: ${audioPath}, falling back to TTS`); + } + + // Fallback to TTS + const utterance = new SpeechSynthesisUtterance(text); + const langForTTS = language as Language; + utterance.lang = langForTTS === 'te' ? 'te-IN' : + langForTTS === 'kn' ? 'kn-IN' : + langForTTS === 'mr' ? 'mr-IN' : + langForTTS === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } + }; + + + // Reset feedback states when game changes + useEffect(() => { + setShowFeedback(false); + setIsCorrectAnswer(false); + setShowTargetState(false); + }, [currentGameIndex]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (previewPhase === 'demo' && demoStep === 'instruction1') { + playNarration(currentGameInstructions.narration1, 1); + } + }, [previewPhase, demoStep, currentGameInstructions.narration1, currentGameIndex]); + + // When instruction 1 narration finishes, move to appropriate wait step + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + if (currentGame === 'soundMatch') { + setDemoStep('waitForSpeaker'); + } else if (currentGame === 'wordDetective' || currentGame === 'pictureWords') { + setDemoStep('waitForReady'); + } + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration, currentGame]); + + // Focus management for accessibility + useEffect(() => { + if (speakerButtonRef.current && (demoStep === 'waitForReady' || demoStep === 'waitForSpeaker')) { + speakerButtonRef.current.focus(); + } + }, [demoStep]); + + // Clean up speech synthesis on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + const showSpeaker = currentGame === 'soundMatch' && (demoStep === 'waitForSpeaker' || demoStep === 'instruction1'); + const showReadyButton = (currentGame === 'wordDetective' || currentGame === 'pictureWords') && (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showWord = (currentGame === 'wordDetective' && (demoStep === 'showWord' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4')) || (currentGame === 'pictureWords' && (demoStep === 'showWord' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4')); + const showOptions = demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + const showTarget = currentGame === 'soundMatch' && showTargetState && (demoStep === 'showTarget' || demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions; + + // Render introduction phase + if (previewPhase === 'introduction') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + } + /> +
+
+ ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + // Render completion phase + if (previewPhase === 'completion') { + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + const handleReplayDemo = () => { + stopAllAudio(); + setCurrentGameIndex(0); + setSuccessfulRuns(0); + setHasCompletedFirstCycle(false); + setPreviewPhase('countdown'); + }; + + return ( + + ); + } + + // Render demo phase + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+
+

+ {contentLanguage === 'en' ? 'How to Play' : contentLanguage === 'te' ? 'ఎలా ఆడాలి' : contentLanguage === 'kn' ? 'ಹೇಗೆ ಆಡುವುದು' : 'कसे खेळायचे'} +

+
+ {currentGameInstructions.title} +
+
+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Transition Text */} + {showTransitionText && ( +
+
+
+ {contentLanguage === 'en' ? 'Next Game:' : contentLanguage === 'te' ? 'తదుపరి గేమ్:' : contentLanguage === 'kn' ? 'ಮುಂದಿನ ಆಟ:' : 'पुढील खेळ:'} +
+
+ {nextGameName} +
+
+
+ )} + + {/* Main Content Container */} +
+ + {!showTransitionText && ( + <> + {/* Show Word Button for Word Detective and Picture Words */} + {showReadyButton && ( +
+
+
+ +
+ + {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+
+ )} + + {/* Speaker Button for Sound Match */} + {showSpeaker && ( +
+
+
+
+ 🔊 +
+
+ {demoStep === 'waitForSpeaker' && ( +
+ 👆 +
+ )} +
+
+ )} + + {currentGame === 'wordDetective' && !showReadyButton && ( + <> + handleOptionClick(isReal ? 'Real Word' : 'Fake Word')} + className="bg-transparent shadow-none border-0 p-0 h-full" + /> + + {/* {showFeedback && ( +
+
+

+ {isCorrectAnswer ? '🎉 Correct!' : '😢 Oops! Wrong!'} +

+
+
+ )} */} + + + )} + + {currentGame === 'soundMatch' && !showSpeaker && ( + <> + {/* Target Display - Show after speaker click */} + {showTarget && ( +
+
+
{(currentGameInstructions as any).demo.target}
+
+
+ )} + + {/* Options Grid - Show after instruction 3 */} + {showOptions && ( + handleOptionClick(optionWord)} + onContinue={() => {}} + className="bg-transparent shadow-none border-0 p-0 h-full" + /> + )} + + )} + + {currentGame === 'pictureWords' && !showReadyButton && ( + ({ + image: option, + word: option, + category: 'animals' + })) || [], + audio: (currentGameInstructions as any).demo.word || '', + complexity: 'easy', + language: contentLanguage + }} + mode="preview" + selectedLanguage={contentLanguage} + showFeedback={showFeedback} + isCorrect={isCorrectAnswer} + selectedOption={null} + isPreview={true} + demoStep={demoStep} + showHandPointer={showOptions && demoStep === 'waitForAnswer' && !showFeedback} + disabled={!(demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete') || showFeedback} + onOptionSelect={(optionWord) => handleOptionClick(optionWord)} + className="bg-transparent shadow-none border-0 p-0 h-full" + /> + )} + + )} +
+
+
+ + {/* Bottom Section - All buttons in one row */} +
+ {/* Left Side - Skip Demo Button */} +
+ +
+ + {/* Center - Previous/Next Navigation - Truly Centered */} +
+ {/* Previous Button */} + + + {/* Game Indicators */} +
+ {games.map((gameKey, index) => { + const isActive = index === currentGameIndex; + const isCompleted = index < currentGameIndex; + + return ( +
+ ); + })} +
+ + {/* Next Button */} + +
+ + {/* Right Side - Placeholder */} +
+ +
+ +
+
+ + ); +} + +export default CombinedWordGamesPreview; \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/ContinueButton.tsx b/src/lib/axl-explorations/src/components/games/ContinueButton.tsx new file mode 100644 index 00000000..cc318755 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ContinueButton.tsx @@ -0,0 +1,53 @@ +import { Button } from "../ui/button"; +import { ArrowRight } from "lucide-react"; + +interface ContinueButtonProps { + onContinue?: () => void; + mode?: 'game' | 'preview'; + isPreview?: boolean; + showContinueButton?: boolean; + className?: string; +} + +export function ContinueButton({ + onContinue, + mode, + isPreview = false, + showContinueButton, + className = '' +}: ContinueButtonProps) { + // Determine if button should be shown + // Priority: showContinueButton (if explicitly passed) > mode === 'game' > !isPreview + // When showContinueButton is explicitly passed (true or false), use that value + // Otherwise, fall back to checking mode or isPreview + const shouldShow = onContinue && ( + showContinueButton !== undefined ? showContinueButton : + mode ? mode === 'game' : + !isPreview + ); + + if (!shouldShow) { + return null; + } + + return ( +
+ +
+ ); +} + diff --git a/src/lib/axl-explorations/src/components/games/FillInBlanksGame.tsx b/src/lib/axl-explorations/src/components/games/FillInBlanksGame.tsx new file mode 100644 index 00000000..188e9996 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/FillInBlanksGame.tsx @@ -0,0 +1,772 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { ArrowLeft, ArrowRight, RotateCcw, CheckCircle, TrendingUp, Globe, Sparkles } from "lucide-react"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { cn } from "../../lib/utils"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import fillInBlanksData from "../../data/fillInBlanksData.json"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import FillInBlanksGamePreview from "./FillInBlanksGamePreview"; +import { FillInBlanksGameCore, type FillInBlanksQuestion } from "./FillInBlanksGameCore"; + +// FillInBlanksQuestion interface is now imported from FillInBlanksGameCore + +interface FillInBlanksGameProps { + onBack: () => void; +} + +export function FillInBlanksGame({ onBack }: FillInBlanksGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + const [levelFailed, setLevelFailed] = useState(false); + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations + const getLanguageLevels = (language: Language) => { + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `fillInBlanks_${selectedLanguage}` : 'fillInBlanks'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + setShowPreview(false); // Hide preview when level is selected + setIsGameComplete(false); + setScore(0); + setTotalCorrect(0); + setCurrentQuestionIndex(0); + setSelectedAnswer(null); + setShowFeedback(false); + setLevelFailed(false); + } + }, [selectedLevel, selectedLanguage]); + + // Initialize game session and questions when language or level is selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + await new Promise(resolve => setTimeout(resolve, 100)); + + console.log(`Starting fill-in-blanks game session for ${selectedLanguage}, level: ${currentLevel}`); + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + const newQuestions = generateFillInBlanksQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + usedQuestions + ); + setQuestions(newQuestions); + console.log(`Generated ${newQuestions.length} fill-in-blanks questions for level ${currentLevel}`); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute effective current level from progress (score > 0 or completed) + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + + const computedFromProgress = Math.min( + Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), + languageLevels.maxLevels + ); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min( + Math.max(computedFromProgress, backendProvided), + languageLevels.maxLevels + ); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setUsedQuestions(new Set()); + } + }, [selectedLevel, selectedLanguage]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + // Enhanced audio function for different languages - REMOVED + // const playAudio = (text: string, language: Language) => { + // speechSynthesis.cancel(); + + // setTimeout(() => { + // const utterance = new SpeechSynthesisUtterance(text); + + // switch (language) { + // case 'te': + // utterance.lang = 'te-IN'; + // utterance.rate = 0.8; + // utterance.pitch = 1.0; + // utterance.volume = 1.0; + // break; + // case 'mr': + // utterance.lang = 'mr-IN'; + // utterance.rate = 0.8; + // utterance.pitch = 1.0; + // utterance.volume = 1.0; + // break; + // default: + // utterance.lang = 'en-US'; + // utterance.rate = 0.8; + // utterance.pitch = 1.0; + // utterance.volume = 0.9; + // } + + // const voices = speechSynthesis.getVoices(); + // let selectedVoice = null; + + // if (language === 'te') { + // selectedVoice = + // voices.find(voice => voice.lang === 'te-IN' || voice.lang === 'te') || + // voices.find(voice => voice.lang === 'hi-IN' || voice.lang === 'hi') || + // voices[0]; + // } else if (language === 'mr') { + // selectedVoice = + // voices.find(voice => voice.lang === 'mr-IN' || voice.lang === 'mr') || + // voices.find(voice => voice.lang === 'hi-IN' || voice.lang === 'hi') || + // voices[0]; + // } else { + // selectedVoice = + // voices.find(voice => voice.lang === 'en-US') || + // voices.find(voice => voice.lang.startsWith('en')) || + // voices[0]; + // } + + // if (selectedVoice) { + // utterance.voice = selectedVoice; + // } + + // speechSynthesis.speak(utterance); + // }, 50); + // }; + + // Auto-play audio when question changes - REMOVED + // useEffect(() => { + // if (currentQuestion && !showFeedback && !isGameComplete && selectedLanguage && + // selectedLevel !== null && !showLevelSelector) { + // const timer = setTimeout(() => { + // if (currentQuestion && !showFeedback && !isGameComplete && selectedLanguage) { + // playAudio(currentQuestion.sentence, selectedLanguage); + // } + // }, 500); + + // return () => { + // clearTimeout(timer); + // speechSynthesis.cancel(); + // }; + // } + // }, [currentQuestionIndex, currentQuestion, showFeedback, isGameComplete, selectedLanguage, selectedLevel, showLevelSelector]); + + // Cleanup speech synthesis on component unmount - REMOVED + // useEffect(() => { + // return () => { + // speechSynthesis.cancel(); + // }; + // }, []); + + const handleAnswerSelect = (answer: string) => { + setSelectedAnswer(answer); + }; + + const checkAnswer = async () => { + if (!selectedAnswer) return; + + const correct = selectedAnswer === currentQuestion.correctAnswer; + setIsCorrect(correct); + setShowFeedback(true); + + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `fillinblanks_${currentLevel}_${currentQuestionIndex}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'fillInBlanks', + selectedAnswer, + currentQuestion.correctAnswer, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'fillInBlanks', + userAnswer: selectedAnswer, + correctAnswer: currentQuestion.correctAnswer, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(score + 1); + setTotalCorrect(totalCorrect + 1); + } + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setShowFeedback(false); + setSelectedAnswer(null); + } else { + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Fill In Blanks Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + console.log(`Fill-in-blanks game completed for ${selectedLanguage}, previous level: ${previousLevel}`); + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const handleBackClick = async () => { + await sessionTelemetryManager.endSubSessionWithBackButton(); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateFillInBlanksQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() + ); + setQuestions(newQuestions); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/fill-blanks-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/fill-blanks-game'); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Fill-in-Blanks Master - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Word Detective - Great Listening!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level selector is requested + if (showLevelSelector) { + const levelSelectorCurrentLevel = gameProgress.currentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Fill in the Blanks" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + if (levelFailed) { + return ( + + ); + } + + return ( + { + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage}`); + manuallyAdvanceLevel(gameKey, nextLevel); + navigate(`/fill-blanks-game/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Fill in the Blanks +

+
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ +
+
+
+
+ ); +} + +// Helper function to map complexity to sentence difficulty +function mapComplexityToSentenceLevel(complexity: string): string { + switch (complexity.toLowerCase()) { + case 'easy': + case 'beginner': + return 'basic'; + case 'medium': + return 'intermediate'; + case 'hard': + return 'advanced'; + case 'expert': + return 'expert'; + case 'master': + return 'master'; + default: + return 'basic'; + } +} + +// Generate fill-in-the-blanks questions from JSON data +function generateFillInBlanksQuestions( + language: Language, + level: number, + complexity: string, + count: number = 10, + usedQuestions: Set = new Set() +): FillInBlanksQuestion[] { + + // Get questions from JSON data + // Ensure only supported languages are passed (fillInBlanksData doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const languageData = fillInBlanksData[supportedLanguage]; + if (!languageData) { + console.warn(`No data found for language: ${language}`); + return []; + } + + const levelKey = mapComplexityToSentenceLevel(complexity); + const questionSet = languageData[levelKey as keyof typeof languageData] || languageData.basic; + + const questions: FillInBlanksQuestion[] = []; + const localUsedQuestions: string[] = []; + + // Filter out questions already used in this session + const availableQuestions = questionSet.filter(question => + !usedQuestions.has(question.sentence) + ); + + // Reset session cache if we've exhausted most questions + let workingQuestions = [...availableQuestions]; + if (workingQuestions.length < count) { + console.log(`🔄 Resetting fill-in-blanks cache for ${language} ${complexity} - only ${workingQuestions.length} fresh questions remaining`); + questionSet.forEach(question => usedQuestions.delete(question.sentence)); + workingQuestions = [...questionSet]; + } + + const actualCount = Math.min(count, workingQuestions.length); + console.log(`🎮 Generating ${actualCount} UNIQUE fill-in-blanks questions for ${language} level ${level} (${complexity})`); + + for (let i = 0; i < actualCount; i++) { + // Filter out questions already used in this question set + const unusedQuestions = workingQuestions.filter(question => + !localUsedQuestions.includes(question.sentence) + ); + + if (unusedQuestions.length === 0) { + console.warn(`⚠️ No more fill-in-blanks options available for ${language} ${complexity}`); + break; + } + + // Pick a random question from unused questions + const randomIndex = Math.floor(Math.random() * unusedQuestions.length); + const question = unusedQuestions[randomIndex]; + + // Track usage + const questionKey = question.sentence; + localUsedQuestions.push(questionKey); + usedQuestions.add(questionKey); + + questions.push({ + sentence: question.sentence, + missingWord: question.missingWord, + correctAnswer: question.correctAnswer, + options: [...question.options].sort(() => Math.random() - 0.5), // Shuffle options + language, + complexity, + level + }); + } + + // Shuffle questions to randomize order + return questions.sort(() => Math.random() - 0.5); +} + +export default FillInBlanksGame; diff --git a/src/lib/axl-explorations/src/components/games/FillInBlanksGameCore.tsx b/src/lib/axl-explorations/src/components/games/FillInBlanksGameCore.tsx new file mode 100644 index 00000000..f1400a09 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/FillInBlanksGameCore.tsx @@ -0,0 +1,187 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { CheckCircle } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { cn } from "../../lib/utils"; +import { ContinueButton } from "./ContinueButton"; + +export interface FillInBlanksQuestion { + sentence: string; + missingWord: string; + correctAnswer: string; + options: string[]; + language: Language; + complexity: string; + level: number; +} + +export interface FillInBlanksGameCoreProps { + currentQuestion: FillInBlanksQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + selectedAnswer?: string | null; + showFeedback?: boolean; + isCorrect?: boolean; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onAnswerSelect: (answer: string) => void; + onCheckAnswer?: () => void; + onContinue?: () => void; + className?: string; +} + +export function FillInBlanksGameCore({ + currentQuestion, + mode, + selectedLanguage, + selectedAnswer = null, + showFeedback = false, + isCorrect = false, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onAnswerSelect, + onCheckAnswer, + onContinue, + className = '' +}: FillInBlanksGameCoreProps) { + const optionsRef = useRef(null); + + const getLocalizedText = (key: string) => { + const texts = { + chooseCorrectWord: { + en: 'Choose the correct word:', + te: 'సరైన పదాన్ని ఎంచుకోండి:', + kn: 'ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ:', + mr: 'योग्य शब्द निवडा:' + }, + checkAnswer: { + en: 'Check Answer', + te: 'సమాధానాన్ని తనిఖీ చేయండి', + kn: 'ಉತ್ತರವನ್ನು ಪರಿಶೀಲಿಸಿ', + mr: 'उत्तर तपासा' + }, + successMessage: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿ!', + mr: '🎉 बरोबर!' + }, + failureMessage: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + return texts[key as keyof typeof texts]?.[selectedLanguage] || texts[key as keyof typeof texts]?.en || ''; + }; + + return ( +
+ {/* Top Content - Balanced Height */} +
+ {/* Top Section - Icon */} +
+
+ 📝 +
+
+ + {/* Middle Section - Sentence with blank */} +
+
+

+ {currentQuestion.sentence.replace(currentQuestion.missingWord, '_____')} +

+
+
+ + {/* Answer Options */} +
+ {!isPreview && ( +

{getLocalizedText('chooseCorrectWord')}

+ )} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+ {currentQuestion.options.map((option, index) => ( + + ))} +
+
+
+
+ + {/* Bottom Section - Check Answer Button and Feedback */} +
+ {/* Check Answer Button */} + {selectedAnswer && !showFeedback && onCheckAnswer && ( +
+ +
+ )} + + {/* Feedback */} + {showFeedback && ( +
+ {isCorrect ? ( +
+

+ {getLocalizedText('successMessage')} +

+
+ ) : ( +
+

{getLocalizedText('failureMessage')}

+
+ )} + + {/* Continue Button - Only show in main game, not preview */} + +
+ )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/FillInBlanksGamePreview.tsx b/src/lib/axl-explorations/src/components/games/FillInBlanksGamePreview.tsx new file mode 100644 index 00000000..e8f0dc61 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/FillInBlanksGamePreview.tsx @@ -0,0 +1,619 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Volume2, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw, Eye } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { FillInBlanksGameCore, type FillInBlanksQuestion } from "./FillInBlanksGameCore"; + +interface FillInBlanksGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" + | 'showSentence' // Show the sentence after ready click + | 'instruction2' // After showing sentence, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Fill in the Blanks", + description: "Listen to the sentence and fill in the missing word!", + steps: [ + "📝 Read the sentence carefully", + "🎯 Choose the correct word to complete it", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a sentence with a missing word. Click 'I'm Ready' when prepared", + instruction2: "Good! Now read this sentence carefully", + instruction3: "Now, choose the correct word to complete the sentence", + instruction4: "Great job! You've completed the demo successfully!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Get ready! You'll see a sentence with a missing word. Click I'm Ready when prepared", + narration2: "Good! Now read this sentence carefully", + narration3: "Now, choose the correct word to complete the sentence", + narration4: "Great job! You've completed the demo successfully!", + howToPlay: "How to Play", + demo: { + sentence: "The _____ is shining bright", + options: ["some", "sun", "son", "sum"], + correctAnswer: "sun", + explanation: "Correct! The sun is shining bright." + } + }, + te: { + title: "ఖాళీలను నింపండి", + description: "వాక్యాన్ని వినండి మరియు తప్పిపోయిన పదాన్ని నింపండి!", + steps: [ + "📝 వాక్యాన్ని జాగ్రత్తగా చదవండి", + "🎯 దాన్ని పూర్తి చేయడానికి సరైన పదాన్ని ఎంచుకోండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు తప్పిపోయిన పదంతో వాక్యాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + instruction3: "ఇప్పుడు, వాక్యాన్ని పూర్తి చేయడానికి సరైన పదాన్ని ఎంచుకోండి", + instruction4: "అద్భుతం! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "సిద్ధంగా ఉండండి! మీరు తప్పిపోయిన పదంతో వాక్యాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + narration3: "ఇప్పుడు, వాక్యాన్ని పూర్తి చేయడానికి సరైన పదాన్ని ఎంచుకోండి", + narration4: "అద్భుతం! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + howToPlay: "ఎలా ఆడాలి", + demo: { + sentence: "_____ తేజస్సుగా వెలుగుతోంది.", + options: ["కొన్ని", "సూర్యుడు", "కుమారుడు", "మొత్తం"], + correctAnswer: "సూర్యుడు", + explanation: "సరైనది! సూర్యుడు తేజస్సుగా వెలుగుతోంది." + } + }, + kn: { + title: "ಖಾಲಿ ಜಾಗಗಳನ್ನು ತುಂಬಿಸಿ", + description: "ವಾಕ್ಯವನ್ನು ಕೇಳಿ ಮತ್ತು ಕಾಣೆಯಾದ ಪದವನ್ನು ತುಂಬಿಸಿ!", + steps: [ + "📝 ವಾಕ್ಯವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "🎯 ಅದನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಕಾಣೆಯಾದ ಪದದೊಂದಿಗೆ ವಾಕ್ಯವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ವಾಕ್ಯವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ಈಗ, ವಾಕ್ಯವನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಓಹ್! ತಪ್ಪು!", + narration1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಕಾಣೆಯಾದ ಪದದೊಂದಿಗೆ ವಾಕ್ಯವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ವಾಕ್ಯವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ಈಗ, ವಾಕ್ಯವನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸರಿಯಾದ ಪದವನ್ನು ಆರಿಸಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + demo: { + sentence: "_____ ಪ್ರಕಾಶಮಾನವಾಗಿ ಹೊಳೆಯುತ್ತಿದೆ.", + options: ["ಕೆಲವು", "ಸೂರ್ಯ", "ಮಗ", "ಮೊತ್ತ"], + correctAnswer: "ಸೂರ್ಯ", + explanation: "ಸರಿ! ಸೂರ್ಯ ಪ್ರಕಾಶಮಾನವಾಗಿ ಹೊಳೆಯುತ್ತಿದೆ." + } + }, + mr: { + title: "रिक्त जागा भरा", + description: "वाक्य ऐका आणि गहाळ शब्द भरा!", + steps: [ + "📝 वाक्य काळजीपूर्वक वाचा", + "🎯 ते पूर्ण करण्यासाठी योग्य शब्द निवडा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "तयार रहा! तुम्ही गहाळ शब्दासह वाक्य पाहाल. तयार असताना 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता हे वाक्य काळजीपूर्वक वाचा", + instruction3: "आता, वाक्य पूर्ण करण्यासाठी योग्य शब्द निवडा", + instruction4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "तयार रहा! तुम्ही गहाळ शब्दासह वाक्य पाहाल. तयार असताना मी तयार आहे क्लिक करा", + narration2: "चांगले! आता हे वाक्य काळजीपूर्वक वाचा", + narration3: "आता, वाक्य पूर्ण करण्यासाठी योग्य शब्द निवडा", + narration4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + howToPlay: "कसे खेळायचे", + demo: { + sentence: "_____ तेजस्वीपणे चमकत आहे.", + options: ["काही", "सूर्य", "मुलगा", "बेरीज"], + correctAnswer: "सूर्य", + explanation: "बरोबर! सूर्य तेजस्वीपणे चमकत आहे." + } + } +}; + +export function FillInBlanksGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: FillInBlanksGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showSentence, setShowSentence] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question object for FillInBlanksGameCore + const demoQuestion: FillInBlanksQuestion = { + sentence: instructions.demo.sentence, + missingWord: '_____', + correctAnswer: instructions.demo.correctAnswer, + options: instructions.demo.options, + language: contentLanguage, + complexity: 'basic', + level: 1 + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined sentence games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined sentence games audio files for Fill in Blanks + const gameName = 'Combined Sentence Games'; + const subGame = 'Fill in Blanks'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + setCurrentStep(0); + break; + case 'waitForReady': + setCurrentStep(0); + break; + case 'showSentence': + setCurrentStep(1); + break; + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + setCurrentStep(2); + break; + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + setCurrentStep(2); + break; + case 'complete': + setCurrentStep(2); + break; + default: + setCurrentStep(0); + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setSelectedAnswer(null); + setShowFeedback(false); + setShowSentence(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle ready button click + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + + // Show sentence + setShowSentence(true); + setDemoStep('showSentence'); + + // Wait a moment, then play instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + // Wait a moment, then play instruction 3 + setTimeout(async () => { + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + // Wait a moment, then show options + setTimeout(() => { + setDemoStep('waitForAnswer'); + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1000); + }, 1000); + }, 1000); + }; + + // Handle option click + const handleOptionClick = async (option: string) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + setSelectedAnswer(option); + setShowFeedback(true); + + const isCorrect = option === instructions.demo.correctAnswer; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + // Show success message for 3 seconds before moving to next step + setTimeout(async () => { + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + }, 3000); // Show success message for 3 seconds + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + // Show failure message for 3 seconds, then allow retry + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 3000); + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowSentence(false); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showSentenceDisplay = showSentence && (demoStep === 'showSentence' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4'); + const showOptions = demoStep === 'waitForAnswer' || demoStep === 'instruction4'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler with audio cleanup + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Start game handler with audio cleanup + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
= 1 ? 'h-[420px]' : 'h-[500px]' + }`}> + {/* Fixed Layout Structure */} +
+ {/* Ready Button - Show initially */} + {(demoStep === 'instruction1' || demoStep === 'waitForReady') && !showSentenceDisplay && ( +
+
+ +
+
} + className={`px-6 py-3 font-bold text-base rounded-full shadow-lg transition-all duration-300 ${ + demoStep === 'waitForReady' + ? 'bg-blue-600 hover:bg-blue-700 text-white hover:scale-105 transform ring-4 ring-blue-400 ring-opacity-50' + : demoStep === 'instruction1' + ? 'bg-gray-300 text-gray-500 cursor-not-allowed opacity-50' + : 'bg-blue-500 text-white cursor-pointer hover:bg-blue-600 hover:scale-105' + }`} + onClick={demoStep === 'waitForReady' || (demoStep !== 'instruction1' && demoStep !== 'complete') ? handleReadyClick : undefined} + tabIndex={demoStep === 'waitForReady' || (demoStep !== 'instruction1' && demoStep !== 'complete') ? 0 : -1} + > + {contentLanguage === 'en' ? "I'm Ready" : contentLanguage === 'te' ? 'నేను సిద్ధంగా ఉన్నాను' : contentLanguage === 'kn' ? 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' : 'मी तयार आहे'} +
+ {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {/* Sentence Display - Show after ready click */} + {(demoStep === 'showSentence' || demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions && ( +
+
+
+ {instructions.demo.sentence} +
+
+
+ )} + + {/* Options Grid - Show after instruction 3 */} + {showOptions && ( + <> + {}} // Empty function for preview + onContinue={() => {}} // Empty function for preview + /> + + + )} +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} + +export default FillInBlanksGamePreview; diff --git a/src/lib/axl-explorations/src/components/games/LetterGame.tsx b/src/lib/axl-explorations/src/components/games/LetterGame.tsx new file mode 100644 index 00000000..112c3097 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/LetterGame.tsx @@ -0,0 +1,1228 @@ +import { useState, useEffect, useCallback, useMemo, useRef } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { LetterGamePreview } from "./LetterGamePreview"; +import { LetterHuntGameCore, type LetterHuntQuestion } from "./LetterHuntGameCore"; +import { ArrowLeft, RotateCcw, TrendingUp, Globe } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { type LetterQuestion } from "../../utils/gameDataGenerators"; +import { memoryGameDataLoader } from "../../utils/memoryGameDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; + +// Extended question interface for multilingual support +interface MultilingualLetterQuestion extends LetterHuntQuestion { + // Additional properties can be added here if needed +} + +interface LetterGameProps { + onBack: () => void; + initialLevel?: number; // Optional: set initial level when used without routing + startLevel?: number; // Optional: start level for level range (takes precedence over initialLevel) + endLevel?: number; // Optional: end level for level range + disableNavigation?: boolean; // Optional: disable React Router navigation + onLevelComplete?: (level: number) => void; // Optional: callback when level completes successfully (called when endLevel is reached) + isShowcase?: boolean; // Optional: if true and level 1 fails, navigate to P1 instead of showing TryAgain + onLevel1Failure?: () => void; // Optional: callback when level 1 fails in showcase mode (deprecated, use onLevelFailure) + onLevelFailure?: (level: number) => void; // Optional: callback when any level fails in showcase mode + customLetters?: string[]; // Optional: custom letters to use instead of level-based letters + confidentLetters?: string[]; // Optional: letters user is confident with (appear less frequently) + sub_session_id?: string; // Optional: Sub session ID for F1 flow + sessionId?: string; // Optional: Session ID + sub_milestone_level?: string; // Optional: Sub milestone level (e.g., "F1") + apply_level?: string; // Optional: Apply level (e.g., "A1", "A2", "A3") + sub_apply_level?: number; // Optional: Sub apply level (1, 2, or 3 - the level within the Apply step) + onA3Pass?: () => void; // Optional: callback when A3 passes and sessionResult is "Pass" + skipPreview?: boolean; // Optional: if true, skip the game preview/demo +} + +export function LetterGame({ onBack, initialLevel, startLevel, endLevel, disableNavigation = false, onLevelComplete, isShowcase = false, onLevel1Failure, onLevelFailure, customLetters, confidentLetters, sub_session_id,sessionId, sub_milestone_level, apply_level, sub_apply_level, onA3Pass, skipPreview = false }: LetterGameProps) { + const navigate = useNavigate(); + const params = useParams<{ level?: string }>(); + const { level: urlLevel } = params || {}; + + // Use startLevel if provided, otherwise use initialLevel + const effectiveStartLevel = startLevel !== undefined ? startLevel : (initialLevel || null); + + // Use internal state for level when disableNavigation is true or no URL level + const [internalLevel, setInternalLevel] = useState(effectiveStartLevel); + + // Use URL level if available and navigation is enabled, otherwise use internal state + const level = disableNavigation ? (internalLevel?.toString() || undefined) : urlLevel; + + // Override navigate function when navigation is disabled + const navigateHandler = disableNavigation + ? (path: string) => { + // Extract level from path like "/letter-game/level/1" + const match = path.match(/\/level\/(\d+)/); + if (match) { + const newLevel = parseInt(match[1]); + setInternalLevel(newLevel); + } + } + : navigate; + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedLetter, setSelectedLetter] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [shouldExitImmediately, setShouldExitImmediately] = useState(false); // Flag to exit without showing SuccessScreen + const [totalCorrect, setTotalCorrect] = useState(0); + // New state for retry flow: track current displayed question (may be retry with shuffled options) + const [currentDisplayedQuestion, setCurrentDisplayedQuestion] = useState(null); + const [totalAttempts, setTotalAttempts] = useState(0); // Track total attempts including retries + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [lives, setLives] = useState(3); // Lives system: 3 red hearts + const [gameEndedByLives, setGameEndedByLives] = useState(false); // Track if game ended due to lives lost + const [assessmentTrackingCreated, setAssessmentTrackingCreated] = useState(false); // Track if assessment tracking has been created for this level completion + const assessmentTrackingCreatedRef = useRef(false); // Use ref for synchronous tracking to prevent race conditions + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations (using English names for all languages) + const getLanguageLevels = (language: Language) => { + switch (language) { + case 'te': + return { + maxLevels: 10, // Standardized to 10 levels for Telugu + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] // English level names + }; + case 'mr': + return { + maxLevels: 10, // Standardized to 10 levels for Marathi + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] // English level names + }; + case 'kn': + return { + maxLevels: 10, // Standardized to 10 levels for Kannada + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] // English level names + }; + default: + return { + maxLevels: 10, // Standard levels for English + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] // English level names + }; + } + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `letterHunt_${selectedLanguage}` : 'letterHunt'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Language data - now uses comprehensive letter sets from JSON + // Note: memoryGameDataLoader only supports en, te, mr, kn (not hi) + // Since selectedLanguage can never be 'hi' (LanguageContext blocks it), this is safe + const getLanguageLetters = (language: Language): string[] => { + // Ensure only supported languages are passed (fallback to 'en' if somehow 'hi' is passed) + const supportedLanguage = (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + return memoryGameDataLoader.getAllLetters(supportedLanguage); + }; + + const getAudioText = (language: Language, letter: string): string => { + // Just return the letter itself for direct pronunciation + return letter; + }; + + // Helper function to get corresponding short/long vowel for Indic languages + const getCorrespondingVowel = (language: Language, letter: string): string | null => { + // Vowel pairs for Indic languages (short/long) + const vowelPairs: Record> = { + te: { + 'అ': 'ఆ', 'ఆ': 'అ', // a/aa + 'ఇ': 'ఈ', 'ఈ': 'ఇ', // i/ii + 'ఉ': 'ఊ', 'ఊ': 'ఉ', // u/uu + 'ఎ': 'ఏ', 'ఏ': 'ఎ', // e/ee + 'ఒ': 'ఓ', 'ఓ': 'ఒ', // o/oo + }, + kn: { + 'ಅ': 'ಆ', 'ಆ': 'ಅ', // a/aa + 'ಇ': 'ಈ', 'ಈ': 'ಇ', // i/ii + 'ಉ': 'ಊ', 'ಊ': 'ಉ', // u/uu + 'ಎ': 'ಏ', 'ಏ': 'ಎ', // e/ee + 'ಒ': 'ಓ', 'ಓ': 'ಒ', // o/oo + }, + mr: { + 'अ': 'आ', 'आ': 'अ', // a/aa + 'इ': 'ई', 'ई': 'इ', // i/ii + 'उ': 'ऊ', 'ऊ': 'उ', // u/uu + 'ए': 'ऐ', 'ऐ': 'ए', // e/ai + 'ओ': 'औ', 'औ': 'ओ', // o/au + 'कि':'की', 'की':'कि', + 'गि':'गी', 'गी':'गि', + 'ति':'ती', 'ती':'ति', + 'लु':'लू', 'लू':'लु', + } + }; + + if (language === 'te' || language === 'kn' || language === 'mr') { + return vowelPairs[language]?.[letter] || null; + } + return null; + }; + + const generateMultilingualQuestions = (language: Language, level: number, complexity: string, count: number = 10): MultilingualLetterQuestion[] => { + // Get level-appropriate letter set from JSON data + const getLevelLetters = (language: Language, level: number): string[] => { + // If customLetters are provided, use them instead of level-based letters + if (customLetters && customLetters.length > 0) { + return customLetters; + } + + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + // Since selectedLanguage can never be 'hi' (LanguageContext blocks it), this is a safety check + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' | 'hi' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn' || language === 'hi') + ? language + : 'en'; + + // For Telugu, Kannada, and Marathi, use exact level mapping + if (supportedLanguage === 'te' || supportedLanguage === 'kn' || supportedLanguage === 'mr' || supportedLanguage === 'en' || supportedLanguage === 'hi') { + const levelKey = level.toString(); + return memoryGameDataLoader.getLettersByLevel(supportedLanguage, levelKey); + } + + // For other languages, use complexity mapping + if (level <= 2) return memoryGameDataLoader.getLetters(supportedLanguage, 'basic'); + if (level <= 4) return memoryGameDataLoader.getLetters(supportedLanguage, 'intermediate'); + if (level <= 6) return memoryGameDataLoader.getLetters(supportedLanguage, 'advanced'); + if (level <= 8) return memoryGameDataLoader.getLetters(supportedLanguage, 'expert'); + return memoryGameDataLoader.getLetters(supportedLanguage, 'master'); + }; + + const lettersToUse = getLevelLetters(language, level); + + // Build weighted letter array based on confidentLetters + const buildWeightedLetterArray = (letters: string[]): string[] => { + if (!confidentLetters || confidentLetters.length === 0 || letters.length === 0) { + return [...letters,...letters]; + } + + // Normalize confident letters to uppercase + const normalizedConfident = confidentLetters + .map((letter: string) => typeof letter === 'string' ? letter.toUpperCase() : '') + .filter(Boolean); + + // Separate letters into confident and non-confident + const confident: string[] = []; + const nonConfident: string[] = []; + + letters.forEach((letter: string) => { + const upperLetter = letter.toUpperCase(); + if (normalizedConfident.includes(upperLetter)) { + confident.push(letter); + } else { + nonConfident.push(letter); + } + }); + + // Build weighted array: + // - Confident letters: appear 1 time (reduced frequency) + // - Non-confident letters: appear 3 times (increased frequency for practice) + const weightedArray: string[] = []; + + // Add confident letters once + confident.forEach((letter) => { + weightedArray.push(letter); + }); + + // Add non-confident letters multiple times (3x for more practice) + for (let i = 0; i < 3; i++) { + nonConfident.forEach((letter) => { + weightedArray.push(letter); + }); + } + + console.log("LetterGame - Weighted array with confident letters:", { + totalLetters: letters.length, + confidentLetters: confident, + nonConfidentLetters: nonConfident, + weightedArrayLength: weightedArray.length, + confidentCount: confident.length, + nonConfidentCount: nonConfident.length * 3 + }); + + return weightedArray.length > 0 ? weightedArray : letters; + }; + + const weightedLetters = buildWeightedLetterArray(lettersToUse); + const questions: MultilingualLetterQuestion[] = []; + + // Strategy with weighted letters: + // Select from weighted array (confident letters appear less, non-confident appear more) + const availableCount = Math.min(weightedLetters.length, count); + const remainingCount = count - availableCount; + + // First phase: Select from weighted array (shuffled) + const shuffledWeighted = [...weightedLetters].sort(() => Math.random() - 0.5); + const selectedTargets = shuffledWeighted.slice(0, availableCount); + + // Second phase: For remaining questions, select from weighted array + const remainingTargets: string[] = []; + for (let i = 0; i < remainingCount; i++) { + const randomIndex = Math.floor(Math.random() * weightedLetters.length); + remainingTargets.push(weightedLetters[randomIndex]); + } + + // Combine all targets + const allTargets = [...selectedTargets, ...remainingTargets]; + + // Shuffle all targets to randomize order + for (let i = allTargets.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [allTargets[i], allTargets[j]] = [allTargets[j], allTargets[i]]; + } + + for (let i = 0; i < count; i++) { + const target = allTargets[i]; + + // Get corresponding vowel to exclude (for Indic languages) + const correspondingVowel = getCorrespondingVowel(language, target); + + // Create options (target + 3 random unique letters) + const options = [target]; + let attempts = 0; + const maxAttempts = lettersToUse.length * 3; // Reasonable upper bound + + while (options.length < 4 ) { // && attempts < maxAttempts + const randomLetter = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + // Exclude if already in options or if it's the corresponding vowel (for Indic languages) + const isExcluded = options.includes(randomLetter) || + (correspondingVowel && randomLetter === correspondingVowel); + if (!isExcluded) { + options.push(randomLetter); + } + attempts++; + } + + // Shuffle options + for (let j = options.length - 1; j > 0; j--) { + const k = Math.floor(Math.random() * (j + 1)); + [options[j], options[k]] = [options[k], options[j]]; + } + + questions.push({ + target, + options, + audio: getAudioText(language, target), + audioText: getAudioText(language, target), + language, + complexity + }); + } + + return questions; + }; + + // Initialize game session and questions when language or level is selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + // Add a small delay to ensure state reset completes first + await new Promise(resolve => setTimeout(resolve, 100)); + + // Reset assessment tracking flag when starting a new level + assessmentTrackingCreatedRef.current = false; + setAssessmentTrackingCreated(false); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Start telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + const newQuestions = generateMultilingualQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute effective current level based on successful progress only + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey: string) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + + const computedFromProgress = Math.min( + Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), + languageLevels.maxLevels + ); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min( + Math.max(computedFromProgress, backendProvided), + languageLevels.maxLevels + ); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setTotalAttempts(0); + setSelectedLetter(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setCurrentDisplayedQuestion(null); + setLives(3); // Reset lives to 3 + setGameEndedByLives(false); // Reset lives lost flag + + // Clear any stored failed level when user navigates to a level + const failedLevelKey = `failedLevel_${gameKey}`; + localStorage.removeItem(failedLevelKey); + } + }, [selectedLevel, selectedLanguage, gameKey]); + + // Helper function to shuffle array (Fisher-Yates algorithm) + const shuffleArray = useCallback((array: T[]): T[] => { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + }, []); + + // Helper function to create a question with shuffled options + const createQuestionWithShuffledOptions = useCallback((question: MultilingualLetterQuestion): MultilingualLetterQuestion => { + return { + ...question, + options: shuffleArray(question.options) + }; + }, [shuffleArray]); + + // Get the current question to display (either from original questions or retry with shuffled options) + const currentQuestion = currentDisplayedQuestion || questions[currentQuestionIndex]; + const progressCurrent = questions.length === 0 + ? 0 + : Math.min(currentQuestionIndex + 1, questions.length); + + // Initialize currentDisplayedQuestion when questions are loaded or question index changes + useEffect(() => { + if (questions.length > 0 && currentQuestionIndex < questions.length) { + const originalQuestion = questions[currentQuestionIndex]; + // Create question with shuffled options for new question + setCurrentDisplayedQuestion(createQuestionWithShuffledOptions(originalQuestion)); + } + }, [questions, currentQuestionIndex, createQuestionWithShuffledOptions]); + + // Track question start time + useEffect(() => { + if (currentQuestion) { + setQuestionStartTime(Date.now()); + } + }, [currentQuestionIndex, currentDisplayedQuestion]); + + // Handle feedback audio completion - if lives reached 0, end game after audio completes + const handleFeedbackAudioComplete = useCallback(() => { + if (lives === 0 && !isGameComplete) { + setGameEndedByLives(true); + } + }, [lives, isGameComplete]); + + // Handle game end when all lives are lost - trigger after audio completes + useEffect(() => { + const handleGameEndByLives = async () => { + if (!gameEndedByLives || isGameComplete || assessmentTrackingCreatedRef.current) return; // Prevent multiple calls + + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + const currentUser = sessionManager.getCurrentUser(); + + if (currentUser) { + // Set flag BEFORE calling API to prevent duplicate calls (use ref for synchronous check) + assessmentTrackingCreatedRef.current = true; + setAssessmentTrackingCreated(true); + + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + // Use sessionId prop if provided, otherwise fallback to telemetry sessionId + const effectiveSessionId = sessionId || currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + // For lives lost scenario: maxScore = totalCorrect, totalScore = totalAttempts + const totalQuestionCount = Math.max(1, currentQuestionIndex + 1); // Questions attempted + const scorePercentage = totalQuestionCount > 0 ? (totalCorrect / totalQuestionCount) * 100 : 0; + + // Get latest question summaries + setQuestionSummaries((latestSummaries) => { + // For F1/F2 flow Apply steps, sub_apply_level should be the current level being played (1, 2, or 3) + // Use currentLevel (the actual level being played) instead of the prop value + const effectiveSubApplyLevel = apply_level ? currentLevel : undefined; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Letter Hunt Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: totalCorrect, // This becomes totalMaxScore (max possible score = number of correct answers) + correctAnswers: 0, // Not used when totalScore is provided, but required by interface + totalScore: totalAttempts, // This becomes totalScore (actual score = number of attempts) + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: effectiveSessionId, + subsessionId: subsessionId, + sub_session_id: sub_session_id, // Pass F1/F2 flow sub session ID + sub_milestone_level: sub_milestone_level, // Pass F1/F2 flow sub milestone level ("F1" or "F2") + apply_level: apply_level, // Pass apply level (A1, A2, A3) from config + sub_apply_level: effectiveSubApplyLevel, // Pass current level within Apply step (1, 2, or 3) + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: true, + scorePercentage: scorePercentage, + totalAttempts: totalAttempts, + totalQuestionsAttempted: totalQuestionCount, // Store actual questions attempted + gameEndedByLives: true, // Flag indicating game ended due to lives lost + livesLost: true + } + }).then((assessmentResponse) => { + // Extract familiarity_syllables from API response and store as confidentLetters + // Only store for Apply steps (A1, A2, A3) + if (apply_level && (apply_level === "A1" || apply_level === "A2" || apply_level === "A3")) { + if (assessmentResponse?.data?.familiarity_syllables && Array.isArray(assessmentResponse.data.familiarity_syllables)) { + const familiaritySyllables = assessmentResponse.data.familiarity_syllables; + // Store in localStorage for use in subsequent LetterHunt components + if (typeof window !== 'undefined') { + localStorage.setItem('confidentLetters', JSON.stringify(familiaritySyllables)); + console.log(`✅ Stored confidentLetters from API response (${apply_level}, lives lost):`, familiaritySyllables); + } + } + } + }).catch((error) => { + console.error("Error in assessment tracking (lives lost):", error); + }); + return latestSummaries; + }); + } + + // End telemetry subsession and flush events + await sessionTelemetryManager.endSubSession(); + await sessionTelemetryManager.flushAssessEventBatch(); + + endSession(); + setLevelFailed(true); + setIsGameComplete(true); + }; + + if (gameEndedByLives && lives === 0 && !isGameComplete) { + handleGameEndByLives(); + } + }, [gameEndedByLives, lives, isGameComplete, assessmentTrackingCreated, currentQuestionIndex, totalCorrect, totalAttempts, levelStartTime, selectedLanguage, currentLevel, difficultySettings.complexity, gameKey, endSession]); + + const handleLetterSelect = async (letter: string) => { + if (showFeedback || isGameComplete) return; + + setSelectedLetter(letter); + const correct = letter === currentQuestion.target; + setIsCorrect(correct); + setShowFeedback(true); + + // Increment total attempts (including retries) + setTotalAttempts(prev => prev + 1); + + // Record the answer for adaptive learning + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `letter_${currentLevel}_${currentQuestionIndex}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'letterHunt', + letter, + currentQuestion.target, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct, totalAttempts + 1); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'letterHunt', + userAnswer: letter, + correctAnswer: currentQuestion.target, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity || 'medium' + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 10); + setTotalCorrect(prevTotal => prevTotal + 1); + } else { + // Lose a life on incorrect answer + // If lives reach 0, feedback audio will play and then handleFeedbackAudioComplete will end the game + setLives(prevLives => Math.max(0, prevLives - 1)); + } + + // Don't auto-advance - player must manually continue + }; + + const handleContinue = useCallback(async () => { + // If game already ended by lives, don't process continue + if (gameEndedByLives || lives === 0) { + return; + } + + const totalQuestionCount = questions.length || 1; + const hasCompletedAllQuestions = totalCorrect >= totalQuestionCount && isCorrect; + + if (hasCompletedAllQuestions) { + // Prevent duplicate assessment tracking calls (use ref for synchronous check) + if (assessmentTrackingCreatedRef.current) { + console.log("Assessment tracking already created for this level, skipping duplicate call"); + return; + } + + const attemptsForPercentage = Math.max(totalAttempts, 1); + const scorePercentage = (totalCorrect / attemptsForPercentage) * 100; + const canAdvance = true; + + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + // Use sessionId prop if provided, otherwise fallback to telemetry sessionId + const effectiveSessionId = sessionId || currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + // Set flag BEFORE calling API to prevent duplicate calls (use ref for synchronous check) + assessmentTrackingCreatedRef.current = true; + setAssessmentTrackingCreated(true); + + setQuestionSummaries((latestSummaries) => { + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + const totalAttemptsLogged = latestSummaries.length; + + // For F1 flow Apply steps, sub_apply_level should be the current level being played (1, 2, or 3) + // Use currentLevel (the actual level being played) instead of the prop value + const effectiveSubApplyLevel = apply_level ? currentLevel : undefined; + + // Call assessment tracking and check for A3 pass asynchronously + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Letter Hunt Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: totalCorrect, // This becomes totalMaxScore (max possible score = correct answers) + correctAnswers: totalAttempts, // This becomes totalScore (actual score = attempts) + totalScore: totalAttempts, // Also pass explicitly (though service calculates from correctAnswers) + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: effectiveSessionId, + subsessionId: subsessionId, + sub_session_id: sub_session_id, // Pass F1 flow sub session ID + sub_milestone_level: sub_milestone_level, // Pass F1 flow sub milestone level ("F1") + apply_level: apply_level, // Pass apply level (A1, A2, A3) from config + sub_apply_level: effectiveSubApplyLevel, // Pass current level within Apply step (1, 2, or 3) + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage, + totalAttempts: totalAttemptsLogged, + gameEndedByLives: false + } + }).then((assessmentResponse) => { + // Extract familiarity_syllables from API response and store as confidentLetters + // Only store for Apply steps (A1, A2, A3) + if (apply_level && (apply_level === "A1" || apply_level === "A2" || apply_level === "A3")) { + if (assessmentResponse?.data?.familiarity_syllables && Array.isArray(assessmentResponse.data.familiarity_syllables)) { + const familiaritySyllables = assessmentResponse.data.familiarity_syllables; + // Store in localStorage for use in subsequent LetterHunt components + if (typeof window !== 'undefined') { + localStorage.setItem('confidentLetters', JSON.stringify(familiaritySyllables)); + console.log(`✅ Stored confidentLetters from API response (${apply_level}):`, familiaritySyllables); + } + } + } + + // Check if this is A3 and sessionResult is "Pass" + if (apply_level === "A3" && assessmentResponse?.data?.sessionResult === "Pass" && onA3Pass) { + console.log("A3 passed with sessionResult: Pass - calling onA3Pass callback"); + onA3Pass(); + } + }).catch((error) => { + console.error("Error in assessment tracking:", error); + }); + + return latestSummaries; + }); + } + + await sessionTelemetryManager.endSubSession(); + await sessionTelemetryManager.flushAssessEventBatch(); + + if (canAdvance) { + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + + setLevelFailed(!canAdvance); + setIsGameComplete(true); + + // Call onLevelComplete callback only when endLevel is reached (or if no endLevel specified) + // For showcase mode with endLevel, show success screen after each level and only call onLevelComplete when all levels are done + if (onLevelComplete && canAdvance && !levelFailed) { + // Only call if we've reached the end level, or if no endLevel is specified + if (endLevel === undefined || currentLevel >= endLevel) { + // If no endLevel, exit immediately without showing SuccessScreen + // This allows the game to exit and move to next P level + if (endLevel === undefined) { + setShouldExitImmediately(true); // Set flag to prevent SuccessScreen from showing + onLevelComplete(currentLevel); + return; // Exit immediately, don't show SuccessScreen + } else { + // If endLevel is reached, delay calling onLevelComplete to allow success screen to show first + // The success screen will render, and after a delay, we'll call onLevelComplete to trigger redirect + setTimeout(() => { + if (onLevelComplete) { + onLevelComplete(currentLevel); + } + }, 2000); // 2 second delay to ensure success screen is visible before redirect + } + } + // If endLevel is specified but we haven't reached it yet, don't call onLevelComplete + // The success screen will show and user can click "Next Level" to continue + } + + return; + } + + // If answer was incorrect and lives remain, show same question with shuffled options (retry) + if (!isCorrect && lives > 0 && currentQuestionIndex < questions.length) { + const originalQuestion = questions[currentQuestionIndex]; + // Create new question with shuffled options for retry + const retryQuestion = createQuestionWithShuffledOptions(originalQuestion); + setCurrentDisplayedQuestion(retryQuestion); + setSelectedLetter(null); + setShowFeedback(false); + // Reset question start time for retry + setQuestionStartTime(Date.now()); + return; + } + + // If answer was correct, move to next original question + if (isCorrect) { + // Move to next original question if available + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedLetter(null); + setShowFeedback(false); + // Reset displayed question for next question + setCurrentDisplayedQuestion(null); + } else { + // No more original questions, but we haven't reached 10 attempts yet + // All questions answered correctly but continue was triggered before state updated + setShowFeedback(false); + setQuestionStartTime(Date.now()); + } + } + }, [currentQuestionIndex, questions.length, totalCorrect, totalAttempts, isCorrect, previousLevel, gameKey, createQuestionWithShuffledOptions, levelStartTime, selectedLanguage, currentLevel, difficultySettings.complexity, getGameProgress, endSession, lives, gameEndedByLives]); + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setTotalAttempts(0); + setSelectedLetter(null); + setShowFeedback(false); + setIsGameComplete(false); + setShouldExitImmediately(false); // Reset exit flag + setShowLevelUp(false); + setLevelFailed(false); + setCurrentDisplayedQuestion(null); + setLives(3); // Reset lives to 3 + setGameEndedByLives(false); // Reset lives lost flag + setAssessmentTrackingCreated(false); // Reset assessment tracking flag + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + // Start new session and regenerate questions + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateMultilingualQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL or update internal state + navigateHandler(`/letter-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + // Navigate to level selector or reset internal state + navigateHandler('/letter-game'); + }; + + // Handle back button with telemetry + const handleBackWithTelemetry = async () => { + if (selectedLevel !== null && !showLevelSelector && !isGameComplete) { + await sessionTelemetryManager.endSubSessionWithBackButton(); + } + onBack(); + }; + + + + const calculateStars = () => { + // Determine stars based on accuracy using actual attempts as the denominator + const attemptsUsed = Math.max(totalAttempts || questions.length || 0, 1); + const percentage = (totalCorrect / attemptsUsed) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { // Only calculate achievements if there are questions + if (totalCorrect === questions.length) { + achievements.push("Letter Master - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Quick Learner - Great Progress!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + // skipPreview allows parent to override and skip preview entirely (e.g., for F1 P2+, F2 P2+) + const shouldShowPreview = !skipPreview && showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selection screen if level not selected (after preview) + if (showLevelSelector) { + // Use the actual current level being played, not just the stored progress level + const levelSelectorCurrentLevel = selectedLevel || gameProgress.currentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Letter Recognition" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + + // If we should exit immediately (no endLevel, onLevelComplete called), don't show SuccessScreen + if (shouldExitImmediately) { + return null; // Exit immediately, onLevelComplete will handle navigation + } + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed or ended by lives, show try again screen + if (levelFailed || gameEndedByLives) { + // For lives lost scenario: totalQuestions = questions attempted + const displayQuestions = gameEndedByLives ? Math.max(1, currentQuestionIndex + 1) : questions.length; + + // If level fails in showcase mode, clicking "Play Again" should exit to Learn step + // Check if it's the start level (could be startLevel or initialLevel or currentLevel === 1) + const effectiveStartLevel = startLevel !== undefined ? startLevel : (initialLevel !== undefined ? initialLevel : 1); + const isStartLevel = currentLevel === effectiveStartLevel; + + // Determine if we should exit (showcase mode + callback provided) + // Use onLevelFailure if provided (for Apply steps), otherwise fall back to onLevel1Failure for backward compatibility + const hasFailureCallback = isShowcase && (onLevelFailure || (isStartLevel && onLevel1Failure)); + const shouldExitOnFailure = hasFailureCallback; + + // Debug logging + console.log("TryAgain - isShowcase:", isShowcase, "currentLevel:", currentLevel, "effectiveStartLevel:", effectiveStartLevel, "isStartLevel:", isStartLevel, "onLevelFailure:", !!onLevelFailure, "onLevel1Failure:", !!onLevel1Failure, "shouldExitOnFailure:", shouldExitOnFailure); + + // Create handleTryAgain function - ensure it's stable and calls the right handler + // IMPORTANT: For showcase mode, we should exit immediately without resetting + const handleTryAgain = () => { + if (shouldExitOnFailure) { + // Exit to Learn step instead of restarting - call immediately without resetting game + console.log(`Showcase level ${currentLevel} failed - redirecting to Learn step (calling failure callback)`); + // Set a flag to prevent any game reset logic from running + setLevelFailed(false); // Clear the failed flag so TryAgain screen disappears + setIsGameComplete(false); // Clear game complete flag + // Call the failure handler which will exit and redirect + // Prefer onLevelFailure (includes level number), fall back to onLevel1Failure for backward compatibility + if (onLevelFailure) { + onLevelFailure(currentLevel); + } else if (onLevel1Failure && isStartLevel) { + onLevel1Failure(); + } + } else { + // Normal reset for non-showcase or no callback + console.log("Normal reset - not showcase or no failure callback"); + resetGame(); + } + }; + + return ( + + ); + } + + // If level passed, show success screen + // Determine if there's a next level (respecting endLevel if specified) + const maxAllowedLevel = endLevel !== undefined ? endLevel : languageLevels.maxLevels; + const hasNextLevelInRange = currentLevel < maxAllowedLevel; + + // For Apply steps (isShowcase with endLevel), when all levels are complete, show "Continue" to redirect + const isApplyStepComplete = isShowcase && endLevel !== undefined && currentLevel >= endLevel && apply_level; + const shouldShowContinue = isApplyStepComplete && onLevelComplete; + + return ( + { + // If Apply step is complete, call onLevelComplete to trigger redirect + if (shouldShowContinue && onLevelComplete) { + console.log(`Apply step ${apply_level} completed - calling onLevelComplete to redirect`); + onLevelComplete(currentLevel); + return; + } + + // ✅ MANUAL LEVEL ADVANCEMENT: Force advance to next level when user clicks "Next Level" + const nextLevel = Math.min(currentLevel + 1, maxAllowedLevel); + + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage} (max: ${maxAllowedLevel}${endLevel !== undefined ? `, range: ${startLevel || initialLevel || 1}-${endLevel}` : ''})`); + + // Manually advance the level using the learning progress hook + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL or update internal state + if (disableNavigation) { + setInternalLevel(nextLevel); + } else { + navigateHandler(`/letter-game/level/${nextLevel}`); + } + + // Clear any stored failed level when user advances to next level + const failedLevelKey = `failedLevel_${gameKey}`; + localStorage.removeItem(failedLevelKey); + + // Reset game state for new level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedLetter(null); + setShowFeedback(false); + setIsCorrect(false); + setShowLevelUp(false); + setLevelFailed(false); + setIsGameComplete(false); + setCurrentDisplayedQuestion(null); + setLives(3); + setGameEndedByLives(false); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + // Regenerate questions for the next level if within endLevel range + if (selectedLanguage && (endLevel === undefined || nextLevel <= endLevel)) { + const session = startSession(gameKey); + const newQuestions = generateMultilingualQuestions( + selectedLanguage, + nextLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + } + + // Don't call onLevelComplete here - it should only be called when the level is actually completed + // (handled in the level completion logic around line 699) + // The "Next Level" button is for transitioning TO the next level, not completing it + }} + continueButtonText={shouldShowContinue ? "Continue" : undefined} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + // Log debug info to help diagnose blank screen issues + console.log("LetterGame - No currentQuestion, showing loading:", { + questionsLength: questions.length, + currentQuestionIndex, + selectedLanguage, + selectedLevel, + currentLevel, + customLetters, + lettersToUse: customLetters && customLetters.length > 0 ? customLetters : 'using level-based', + }); + return ( +
+
Loading questions...
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ {/* Back button removed - using justify-center instead of justify-between */} +
+

+ Letter Recognition +

+ {/*
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Level ${selectedLevel}` : + `Level ${currentLevel}` + } + +
*/} +
+ + {/* */} +
+ + {/* Main Content Card */} +
+ {/* Game Core Component */} + +
+
+
+ ); +} + +export default LetterGame; \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/LetterGamePreview.tsx b/src/lib/axl-explorations/src/components/games/LetterGamePreview.tsx new file mode 100644 index 00000000..c28a15d9 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/LetterGamePreview.tsx @@ -0,0 +1,708 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { + ArrowLeft, + Volume2, + Sparkles, + Clock, + CheckCircle, + Gamepad2, + RotateCcw, +} from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { + playAudio, + playTTS, + playSuccessSound, + playFailureSound, + stopAllAudio, + isAudioStopped, + trackAudio, + attachSlowLoadToast, +} from "../../utils/audioUtils"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { + LetterHuntGameCore, + type LetterHuntQuestion, +} from "./LetterHuntGameCore"; +import { setLocalData } from "../../../../../utils/constants"; + +interface LetterGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = "countdown" | "demo" | "completion"; + +type DemoStep = + | "instruction1" // Show instruction 1, play narration + | "waitForSpeaker" // Show speaker icon, wait for user click + | "instruction2" // After speaker clicked, show instruction 2, play narration + | "instruction3" // After instruction 2, show instruction 3, play narration + | "waitForAnswer" // Show options, wait for user to select + | "wrongAnswer" // User selected wrong answer + | "instruction4" // After correct answer, show final instruction + | "complete"; // Demo run complete + +const gameInstructions = { + en: { + title: "Letter Recognition", + description: "Listen to the letter sound and find the matching letter!", + steps: [ + "🔊 Click the speaker to hear the letter sound", + "🎯 Click on the correct letter", + "✨ Get points for correct answers!", + ], + instruction1: "Click the speaker icon to hear the letter sound", + instruction2: "Listen carefully to the letter sound", + instruction3: "Now, click on the matching letter from the options below", + instruction4: "Great job! You've completed the demo successfully!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Click the speaker icon to hear the letter sound", + narration2: "Listen carefully to the letter sound", + narration3: "Now, click on the matching letter from the options below", + narration4: "Great job! You've completed the demo successfully!", + howToPlay: "How to Play", + demo: { + audio: "A", + options: ["A", "B", "C", "D"], + correctAnswer: "A", + explanation: "The sound 'A' matches the letter A!", + }, + }, + te: { + title: "అక్షర గుర్తింపు", + description: "అక్షర ధ్వనిని విని సరిపోయే అక్షరాన్ని కనుగొనండి!", + steps: [ + "🔊 స్పీకర్‌ను క్లిక్ చేసి అక్షర ధ్వనిని వినండి", + "🎯 సరైన అక్షరాన్ని క్లిక్ చేయండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!", + ], + instruction1: "అక్షర ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + instruction2: "అక్షర ధ్వనిని జాగ్రత్తగా వినండి", + instruction3: + "ఇప్పుడు, క్రింది ఎంపికల నుండి సరిపోయే అక్షరంపై క్లిక్ చేయండి", + instruction4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "అక్షర ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + narration2: "అక్షర ధ్వనిని జాగ్రత్తగా వినండి", + narration3: "ఇప్పుడు, క్రింది ఎంపికల నుండి సరిపోయే అక్షరంపై క్లిక్ చేయండి", + narration4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + howToPlay: "ఎలా ఆడాలి", + demo: { + audio: "అ", + options: ["అ", "ఆ", "ఇ", "ఈ"], + correctAnswer: "అ", + explanation: "ధ్వని 'అ' అక్షరం 'అ'కు సరిపోతుంది!", + }, + }, + kn: { + title: "ಅಕ್ಷರ ಗುರುತಿಸುವಿಕೆ", + description: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಿ ಸರಿಪೋಯುವ ಅಕ್ಷರವನ್ನು ಹುಡುಕಿ!", + steps: [ + "🔊 ಸ್ಪೀಕರ್ ಅನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಿ", + "🎯 ಸರಿಯಾದ ಅಕ್ಷರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!", + ], + instruction1: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಆಲಿಸಿ", + instruction3: "ಈಗ, ಕೆಳಗಿನ ಆಯ್ಕೆಗಳಿಂದ ಹೊಂದಿಕೆಯಾಗುವ ಅಕ್ಷರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿಯಿದೆ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಅಕ್ಷರ ಶಬ್ದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಆಲಿಸಿ", + narration3: "ಈಗ, ಕೆಳಗಿನ ಆಯ್ಕೆಗಳಿಂದ ಹೊಂದಿಕೆಯಾಗುವ ಅಕ್ಷರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + demo: { + audio: "ಅಂ", + options: ["ಅಂ", "ಆ", "ಇ", "ಈ"], + correctAnswer: "ಅಂ", + explanation: "ಶಬ್ದ 'ಅಂ' ಅಕ್ಷರ 'ಅಂ'ಗೆ ಸರಿಪೋತುತ್ತದೆ!", + }, + }, + mr: { + title: "अक्षर ओळख", + description: "अक्षर आवाज ऐकून जुळणारे अक्षर शोधा!", + steps: [ + "🔊 स्पीकरवर क्लिक करून अक्षर आवाज ऐका", + "🎯 योग्य अक्षरावर क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!", + ], + instruction1: "अक्षर आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + instruction2: "अक्षर आवाज काळजीपूर्वक ऐका", + instruction3: "आता, खालील पर्यायांमधून जुळणारे अक्षर क्लिक करा", + instruction4: "छान केलं! तुम्ही डेमो यशस्वीपणे पूर्ण केला आहे!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "अक्षर आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + narration2: "अक्षर आवाज काळजीपूर्वक ऐका", + narration3: "आता, खालील पर्यायांमधून जुळणारे अक्षर क्लिक करा", + narration4: "छान केलं! तुम्ही डेमो यशस्वीपणे पूर्ण केला आहे!", + howToPlay: "कसे खेळायचे", + demo: { + audio: "अ", + options: ["अ", "आ", "इ", "ई"], + correctAnswer: "अ", + explanation: "आवाज 'अ' अक्षर 'अ'शी जुळतो!", + }, + }, + hi: { + title: "अक्षर ओळख", + description: "अक्षर आवाज ऐकून जुळणारे अक्षर शोधा!", + steps: [ + "🔊 स्पीकरवर क्लिक करून अक्षर आवाज ऐका", + "🎯 योग्य अक्षरावर क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!", + ], + demo: { + audio: "अ", + options: ["अ", "आ", "इ", "ई"], + correctAnswer: "अ", + explanation: "आवाज 'अ' अक्षर 'अ'शी जुळतो!", + }, + }, +}; + +export function LetterGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false, +}: LetterGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState("countdown"); + const [demoStep, setDemoStep] = useState("instruction1"); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedSpeaker, setHasClickedSpeaker] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const speakerButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || "en"; + const contentLanguage = selectedLanguage || "en"; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question for preview + const demoQuestion: LetterHuntQuestion = { + target: instructions.demo.correctAnswer, + options: instructions.demo.options, + audio: instructions.demo.audio, + audioText: instructions.demo.audio, + language: contentLanguage, + complexity: "easy", + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase("demo"); + setDemoStep("instruction1"); + }; + + // Play narration using combined letter games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn("Narration already playing, skipping..."); + return; + } + + setIsPlayingNarration(true); + + // Use combined letter games audio files for Letter Hunt + const gameName = "Combined Letter Games"; + const subGame = "Letter Hunt"; + + try { + await playAudio( + { + gameName, + subGame, + language: audioLanguage, + type: "narration", + step, + }, + text + ); + } catch (error) { + console.warn("Audio playback failed, using TTS fallback:", error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Play audio sound from .wav files with TTS fallback (same as combined games) + const playAudioSound = async (text: string): Promise => { + return new Promise((resolve) => { + // Determine the correct audio path based on language + let audioPath = ""; + if (contentLanguage === "te") { + audioPath = `/audio/telugu/letter/${text}.wav`; + } else if (contentLanguage === "kn") { + audioPath = `/audio/kannada/letter/${text}.wav`; + } else if (contentLanguage === "mr") { + audioPath = `/audio/marathi/letter/${text}.wav`; + } else { + // Default to English for other languages + audioPath = `/audio/english/letter/${text}.wav`; + } + + const audio = new Audio(audioPath); + + // Track this audio instance + trackAudio(audio); + attachSlowLoadToast(audio); + + audio + .play() + .then(() => { + // Wait for audio to finish playing + audio.onended = () => { + resolve(); + }; + audio.onerror = () => { + console.warn( + `Audio file not found: ${audioPath}, falling back to TTS` + ); + // Fallback to TTS if .wav file doesn't exist + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = + contentLanguage === "te" + ? "te-IN" + : contentLanguage === "kn" + ? "kn-IN" + : contentLanguage === "mr" + ? "mr-IN" + : contentLanguage === "hi" + ? "hi-IN" + : "en-US"; + utterance.rate = 1.0; + utterance.pitch = 1.0; + + utterance.onend = () => { + resolve(); + }; + + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } else { + resolve(); + } + }; + }) + .catch((error) => { + console.warn( + `Audio file not found: ${audioPath}, falling back to TTS` + ); + // Fallback to TTS if .wav file doesn't exist + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = + contentLanguage === "te" + ? "te-IN" + : contentLanguage === "kn" + ? "kn-IN" + : contentLanguage === "mr" + ? "mr-IN" + : contentLanguage === "hi" + ? "hi-IN" + : "en-US"; + utterance.rate = 1.0; + utterance.pitch = 1.0; + + utterance.onend = () => { + resolve(); + }; + + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } else { + resolve(); + } + }); + }); + }; + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case "instruction1": + case "waitForSpeaker": + setCurrentStep(0); + break; + case "instruction3": + case "waitForAnswer": + setCurrentStep(1); + break; + case "instruction4": + case "complete": + setCurrentStep(2); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === "instruction1" && previewPhase === "demo") { + playNarration(instructions.narration1, 1); + setHasClickedSpeaker(false); + setSelectedAnswer(null); + setShowFeedback(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForSpeaker + useEffect(() => { + if (demoStep === "instruction1" && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep("waitForSpeaker"); + // Focus on speaker button after a brief delay + setTimeout(() => { + speakerButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle speaker button click + const handleSpeakerClick = async () => { + if (demoStep !== "waitForSpeaker" || hasClickedSpeaker) return; + + setHasClickedSpeaker(true); + + // Play the demo audio using the same logic as the core component + await playAudioSound(instructions.demo.audio); + + // Wait a moment for the audio to play, then show instruction3 + setTimeout(async () => { + setDemoStep("instruction3"); + await playNarration(instructions.narration3, 3); + + setDemoStep("waitForAnswer"); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle option click + const handleOptionClick = async (option: string) => { + if (demoStep !== "waitForAnswer" || showFeedback) return; + + setSelectedAnswer(option); + setShowFeedback(true); + + const isCorrect = option === instructions.demo.correctAnswer; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + // Play success sound + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + // Show instruction 4 and play its narration + setDemoStep("instruction4"); + await playNarration(instructions.narration4, 4); + + // Mark demo run as complete and increment completion count + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase("completion"); + }, 2000); + } else { + // Play failure sound first, then feedback message audio will play after + // The feedback audio is handled by LetterHuntGameCore component + // We reset feedback UI only after feedback audio completes (via onFeedbackAudioComplete callback) + await playFailureSound(audioLanguage, { exactLanguage: true }); + // Note: Feedback UI reset happens in onFeedbackAudioComplete callback (see LetterHuntGameCore usage below) + } + }; + + // Restart demo (for second run or Help replay) + const restartDemo = () => { + setDemoStep("instruction1"); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedSpeaker(false); + setCurrentStep(0); + }; + + // Help button click - replay demo without affecting success count + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener("beforeunload", handleBeforeUnload); + + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, []); + + // Show speaker icon - always visible but disabled initially + const showSpeaker = + demoStep === "waitForSpeaker" || + demoStep === "instruction1" || + demoStep === "instruction3" || + demoStep === "waitForAnswer" || + demoStep === "instruction4" || + demoStep === "complete"; + + // Show options only after instruction 3 + const showOptions = + demoStep === "waitForAnswer" || + demoStep === "instruction4" || + demoStep === "complete"; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Start game handler + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase("countdown"); + }; + + // Render completion phase + if (previewPhase === "completion") { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === "countdown") { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ {/* Back button removed */} +
+

+ {instructions.title} +

+
+ + + {contentLanguage === "en" + ? "Level" + : contentLanguage === "te" + ? "స్థాయి" + : contentLanguage === "kn" + ? "ಮಟ್ಟ" + : "पातळी"}{" "} + {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+ {}} // No continue in preview + onSpeakerClick={handleSpeakerClick} + onFeedbackAudioComplete={() => { + /** + * Callback: Called when feedback audio sequence completes + * This ensures UI resets only after full feedback message audio finishes playing + * Sequence: failure sound → feedback1 ("chosen letter is") → selected letter → feedback2 ("try again") + */ + setShowFeedback(false); + setSelectedAnswer(null); + }} + showSpeaker={showSpeaker} + showContinueButton={false} + showProgress={false} + isPreview={true} + demoStep={demoStep} + hasClickedSpeaker={hasClickedSpeaker} + speakerButtonRef={speakerButtonRef} + optionsRef={optionsRef} + showHandPointer={ + showOptions && demoStep === "waitForAnswer" && !showFeedback + } + disabled={demoStep !== "waitForAnswer" || showFeedback} + className="bg-transparent shadow-none border-0 p-0 h-full" + audioLanguageOverride={audioLanguage} + /> +
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} diff --git a/src/lib/axl-explorations/src/components/games/LetterHuntGameCore.tsx b/src/lib/axl-explorations/src/components/games/LetterHuntGameCore.tsx new file mode 100644 index 00000000..1c7b1a72 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/LetterHuntGameCore.tsx @@ -0,0 +1,923 @@ +import { useState, useEffect, useRef, useCallback, useLayoutEffect } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { teluguAudioManager } from "../../utils/teluguAudioManager"; +import { kannadaAudioManager } from "../../utils/kannadaAudioManager"; +import { marathiAudioManager } from "../../utils/marathiAudioManager"; +import { ContinueButton } from "./ContinueButton"; +import { englishAudioManager } from "../../utils/englishAudioManager"; +import { playSuccessSound, attachSlowLoadToast } from "../../utils/audioUtils"; +import { hindiAudioManager } from "../../utils/hindiAudioManager"; +import { getFontFamilyByLang } from "../../../../../utils/fontUtils"; + +// Core question interface for Letter Hunt +export interface LetterHuntQuestion { + target: string; + options: string[]; + audio: string; + audioText: string; + language: Language; + complexity?: string; +} + +interface LetterHuntGameCoreProps { + // Core game props + questions: LetterHuntQuestion[]; + currentQuestionIndex: number; + selectedAnswer: string | null; + showFeedback: boolean; + isCorrect: boolean; + + // Mode configuration + mode: 'game' | 'preview'; + + // Event handlers + onAnswerSelect: (answer: string) => void; + onContinue: () => void; + onSpeakerClick?: () => void; + /** + * Optional callback invoked when feedback audio sequence completes + * Used by preview components to reset UI after full feedback message audio finishes + * Sequence: feedback1 → selected letter → feedback2 + */ + onFeedbackAudioComplete?: () => void; + + // UI customization + showSpeaker?: boolean; + showContinueButton?: boolean; + showProgress?: boolean; + progress?: { + current: number; + total: number; + score?: number; + }; + + // Preview-specific props + isPreview?: boolean; + demoStep?: string; + hasClickedSpeaker?: boolean; + speakerButtonRef?: React.RefObject; + optionsRef?: React.RefObject; + showHandPointer?: boolean; + disabled?: boolean; + + // Styling + className?: string; + // Container control: default 'card' to preserve existing behavior + useContainer?: 'card' | 'none'; + + // Lives system + lives?: number; + maxLives?: number; + audioLanguageOverride?: Language; +} + +export function LetterHuntGameCore({ + questions, + currentQuestionIndex, + selectedAnswer, + showFeedback, + isCorrect, + mode, + onAnswerSelect, + onContinue, + onSpeakerClick, + onFeedbackAudioComplete, + showSpeaker = true, + showContinueButton = true, + showProgress = false, + progress, + isPreview = false, + demoStep, + hasClickedSpeaker = false, + speakerButtonRef, + optionsRef, + showHandPointer = false, + disabled = false, + className = "", + useContainer = 'card', + lives, + maxLives = 3, + audioLanguageOverride +}: LetterHuntGameCoreProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const effectiveLanguage = audioLanguageOverride || selectedLanguage; + const currentQuestion = questions[currentQuestionIndex]; + const [isFeedbackAudioPlaying, setIsFeedbackAudioPlaying] = useState(false); + + // Get font family based on current language (for Telugu support) + const fontFamily = getFontFamilyByLang(effectiveLanguage || selectedLanguage); + + // Track active audio instances for stopping playback when needed + // This allows us to stop all audio (including feedback audio) when user clicks "Next" + const activeAudioRefs = useRef>(new Set()); + const activeTTSRefs = useRef>(new Set()); + const isAudioStoppedRef = useRef(false); + + /** + * Stop all audio playback (audio files and TTS) + * Used when user clicks "Next" button or component unmounts + */ + const stopAllAudio = useCallback(() => { + isAudioStoppedRef.current = true; + + // Stop all HTMLAudioElement instances + activeAudioRefs.current.forEach(audio => { + audio.pause(); + audio.currentTime = 0; + }); + activeAudioRefs.current.clear(); + + // Stop all Text-to-Speech instances + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + activeTTSRefs.current.clear(); + + // Reset flag after a delay to allow new audio to start + setTimeout(() => { + isAudioStoppedRef.current = false; + }, 100); + }, []); + + /** + * Handle continue button click - stops all audio before proceeding + * This ensures feedback audio doesn't continue playing after user moves to next question + */ + const handleContinue = useCallback(() => { + stopAllAudio(); + if (onContinue) { + onContinue(); + } + }, [onContinue, stopAllAudio]); + + const getFeedbackLanguage = useCallback( + (fallback: Language = 'en') => { + if (mode === 'game') { + return (audioLanguageOverride || selectedAudioLanguage || selectedLanguage || fallback) as Language; + } + return (audioLanguageOverride || effectiveLanguage || fallback) as Language; + }, + [audioLanguageOverride, effectiveLanguage, mode, selectedAudioLanguage, selectedLanguage] + ); + + // Localized feedback message parts + // Memoized to prevent unnecessary re-renders + // In game mode: Uses audio instruction language for feedback text + // In preview mode: Uses effectiveLanguage (for preview compatibility) + const getFeedbackText = useCallback((key: 'chosenLetterIs' | 'tryAgain') => { + const texts = { + chosenLetterIs: { + en: 'This is', + te: 'ఇది', + kn: 'ಇದು', + mr: 'हे', + hi: 'यह' + }, + tryAgain: { + en: 'try again', + te: 'మళ్లీ ప్రయత్నించండి', + kn: 'ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ', + mr: 'आहे, पुन्हा प्रयत्न करा', + hi: 'है, फिर कोशिश करें' + } + }; + // Determine language with shared helper (ensures consistent fallback) + const lang = getFeedbackLanguage('en'); + return texts[key][lang] || texts[key].en; + }, [getFeedbackLanguage]); + + // Enhanced audio function for different languages + const playAudio = async (text: string, language: Language): Promise => { + // Check if audio was stopped + if (isAudioStoppedRef.current) { + return; + } + + return new Promise((resolve) => { + // For Telugu, try to use local audio files first + if (language === 'te') { + const audioUrl = teluguAudioManager.getAudioUrl(text); + const audio = new Audio(audioUrl); + activeAudioRefs.current.add(audio); + attachSlowLoadToast(audio); + + audio.onloadeddata = () => { + if (isAudioStoppedRef.current) { + resolve(); + return; + } + audio.play().then(() => { + audio.onended = () => { + activeAudioRefs.current.delete(audio); + resolve(); + }; + }).catch(() => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + audio.onerror = () => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }; + return; + } + if (language === 'hi') { + const audioUrl = hindiAudioManager.getAudioUrl(text); + const audio = new Audio(audioUrl); + activeAudioRefs.current.add(audio); + attachSlowLoadToast(audio); + + audio.onloadeddata = () => { + if (isAudioStoppedRef.current) { + resolve(); + return; + } + audio.play().then(() => { + audio.onended = () => { + activeAudioRefs.current.delete(audio); + resolve(); + }; + }).catch(() => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + audio.onerror = () => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }; + return; + } + // For Kannada, try to use local audio files first + if (language === 'kn') { + const audioUrl = kannadaAudioManager.getAudioUrl(text); + const audio = new Audio(audioUrl); + activeAudioRefs.current.add(audio); + attachSlowLoadToast(audio); + + audio.onloadeddata = () => { + if (isAudioStoppedRef.current) { + resolve(); + return; + } + audio.play().then(() => { + audio.onended = () => { + activeAudioRefs.current.delete(audio); + resolve(); + }; + }).catch(() => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + audio.onerror = () => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }; + return; + } + + // For English, try to use local audio files first + if (language === 'en') { + const audioUrl = englishAudioManager.getAudioUrl(text); + const audio = new Audio(audioUrl); + activeAudioRefs.current.add(audio); + attachSlowLoadToast(audio); + + audio.onloadeddata = () => { + if (isAudioStoppedRef.current) { + resolve(); + return; + } + audio.play().then(() => { + audio.onended = () => { + activeAudioRefs.current.delete(audio); + resolve(); + }; + }).catch(() => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + audio.onerror = () => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }; + return; + } + + // For Marathi, try to use local audio files first + if (language === 'mr') { + const audioUrl = marathiAudioManager.getAudioUrl(text); + const audio = new Audio(audioUrl); + activeAudioRefs.current.add(audio); + attachSlowLoadToast(audio); + + audio.onloadeddata = () => { + if (isAudioStoppedRef.current) { + resolve(); + return; + } + audio.play().then(() => { + audio.onended = () => { + activeAudioRefs.current.delete(audio); + resolve(); + }; + }).catch(() => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + audio.onerror = () => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS + if (!isAudioStoppedRef.current) { + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }; + return; + } + + // Fallback to TTS for all other languages + if (!isAudioStoppedRef.current) { + console.log("playing TTS", text, language); + playTTSAudio(text, language).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + const playTTSAudio = (text: string, language: Language): Promise => { + // Check if audio was stopped + if (isAudioStoppedRef.current) { + return Promise.resolve(); + } + + return new Promise((resolve) => { + const utterance = new SpeechSynthesisUtterance(text); + activeTTSRefs.current.add(utterance); + + // Language-specific settings + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 0.6; // Slower for Telugu + utterance.pitch = 0.9; + utterance.volume = 0.9; + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 0.6; // Slower for Marathi + utterance.pitch = 0.9; + utterance.volume = 0.9; + break; + case 'kn': + utterance.lang = 'kn-IN'; + utterance.rate = 0.6; // Slower for Kannada + utterance.pitch = 0.9; + utterance.volume = 0.9; + break; + default: + utterance.lang = 'en-US'; + utterance.rate = 0.8; + utterance.pitch = 1.0; + utterance.volume = 0.8; + } + + // Set up event handlers + utterance.onend = () => { + activeTTSRefs.current.delete(utterance); + resolve(); + }; + utterance.onerror = () => { + activeTTSRefs.current.delete(utterance); + resolve(); // Resolve even on error to prevent hanging + }; + + // Try to find the best voice (with fallback for voice loading) + const findAndSetVoice = () => { + const voices = speechSynthesis.getVoices(); + let bestVoice = null; + + if (voices.length === 0) { + // Voices not loaded yet, will use default + return; + } + + if (language === 'te') { + bestVoice = voices.find(voice => + voice.lang.includes('te') || + voice.lang.includes('hi-IN') || + voice.lang.includes('en-IN') + ); + } else if (language === 'mr') { + bestVoice = voices.find(voice => + voice.lang.includes('mr') || + voice.lang.includes('hi-IN') || + voice.lang.includes('en-IN') + ); + } else if (language === 'kn') { + bestVoice = voices.find(voice => + voice.lang.includes('kn') || + voice.lang.includes('hi-IN') || + voice.lang.includes('en-IN') + ); + } else { + bestVoice = voices.find(voice => + voice.lang.includes('en-US') || + voice.lang.includes('en-GB') + ); + } + + if (bestVoice) { + utterance.voice = bestVoice; + } + }; + + findAndSetVoice(); + + // Check again before speaking + if (isAudioStoppedRef.current) { + activeTTSRefs.current.delete(utterance); + resolve(); + return; + } + + speechSynthesis.speak(utterance); + + // Timeout to prevent hanging if TTS doesn't fire onend + setTimeout(() => { + if (activeTTSRefs.current.has(utterance)) { + activeTTSRefs.current.delete(utterance); + resolve(); + } + }, 5000); // 5 second timeout for TTS + }); + }; + + // Handle speaker button click + const handleSpeakerClick = async () => { + if (!currentQuestion) return; + + // In preview mode, allow speaker clicks based on demoStep, not disabled prop + if (mode === 'preview') { + if (demoStep !== 'waitForSpeaker' && demoStep !== 'instruction3' && demoStep !== 'waitForAnswer') { + return; + } + } else if (disabled) { + return; + } + + if (onSpeakerClick) { + onSpeakerClick(); + } else { + // Default behavior - play the letter sound + await playAudio(currentQuestion.audioText, effectiveLanguage); + } + }; + + /** + * Play feedback audio sequence when incorrect answer is shown + * Sequence: feedback1 ("chosen letter is") → selected letter → feedback2 ("try again") + * + * This function handles sequential playback of feedback audio files and letter audio, + * ensuring no overlapping. It also calls onFeedbackAudioComplete callback when done, + * which allows parent components (like preview) to reset UI after audio completes. + * + * In game mode: Uses audio instruction language for feedback messages + * In preview mode: Uses audioLanguageOverride or selectedLanguage (for preview compatibility) + */ + const playFeedbackAudio = useCallback(async () => { + if (!currentQuestion || !selectedAnswer || !selectedLanguage) return; + + // In game mode, use audio instruction language for feedback; in preview, use override or game language + const feedbackLanguage = getFeedbackLanguage(); + + setIsFeedbackAudioPlaying(true); + + try { + /** + * Helper function to play a single feedback audio file (feedback1 or feedback2) + * Falls back to TTS if audio file fails to load or play + */ + const playFeedbackAudioFile = async (feedbackNumber: 1 | 2): Promise => { + // Early return if audio was stopped (e.g., user clicked "Next") + if (isAudioStoppedRef.current) { + return; + } + + return new Promise((resolve) => { + const audioPath = `/audio/letter-hunt-incorrect-message/${feedbackLanguage}/feedback${feedbackNumber}.wav`; + const audio = new Audio(audioPath); + activeAudioRefs.current.add(audio); + attachSlowLoadToast(audio); + + audio.onloadeddata = () => { + // Check again before playing (audio might have been stopped while loading) + if (isAudioStoppedRef.current) { + activeAudioRefs.current.delete(audio); + resolve(); + return; + } + audio.play().then(() => { + audio.onended = () => { + activeAudioRefs.current.delete(audio); + resolve(); + }; + }).catch(() => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS if audio file fails to play + if (!isAudioStoppedRef.current) { + const fallbackText = getFeedbackText( + feedbackNumber === 1 ? 'chosenLetterIs' : 'tryAgain' + ); + playTTSAudio(fallbackText, feedbackLanguage).then(() => resolve()); + } else { + resolve(); + } + }); + }; + + audio.onerror = () => { + activeAudioRefs.current.delete(audio); + // Fallback to TTS if audio file doesn't exist or fails to load + if (!isAudioStoppedRef.current) { + const fallbackText = getFeedbackText( + feedbackNumber === 1 ? 'chosenLetterIs' : 'tryAgain' + ); + playTTSAudio(fallbackText, feedbackLanguage).then(() => resolve()); + } else { + resolve(); + } + }; + + // Safety timeout to prevent hanging if audio doesn't fire onended event + setTimeout(() => { + if (activeAudioRefs.current.has(audio) && !audio.ended) { + audio.pause(); + activeAudioRefs.current.delete(audio); + resolve(); + } + }, 10000); + }); + }; + + // Play feedback sequence sequentially: + // 1. "chosen letter is" (feedback1) + // 2. Selected letter audio + // 3. "try again" (feedback2) + // Check if stopped before each step to allow immediate stopping + if (!isAudioStoppedRef.current) { + await playFeedbackAudioFile(1); + } + if (!isAudioStoppedRef.current) { + await playAudio(selectedAnswer, selectedLanguage || currentQuestion.language); + } + if (!isAudioStoppedRef.current) { + await playFeedbackAudioFile(2); + } + + // Notify parent component that feedback audio sequence is complete + // This is used by preview components to reset UI after audio finishes + if (!isAudioStoppedRef.current && onFeedbackAudioComplete) { + onFeedbackAudioComplete(); + } + } catch (error) { + console.warn('Feedback audio playback failed:', error); + } finally { + setIsFeedbackAudioPlaying(false); + } + }, [currentQuestion, selectedAnswer, selectedLanguage, getFeedbackLanguage, getFeedbackText, onFeedbackAudioComplete]); + + // Handle option selection + const handleOptionClick = (option: string) => { + if (disabled || showFeedback) return; + onAnswerSelect(option); + }; + + // Auto-play audio when question changes (only during actual gameplay, not preview) + useEffect(() => { + if (currentQuestion && !showFeedback && mode === 'game' && selectedLanguage && !isPreview) { + // Stop any existing audio before playing new question audio + stopAllAudio(); + // Small delay to ensure component is rendered + const timer = setTimeout(() => { + playAudio(currentQuestion.audioText, effectiveLanguage).catch((error) => { + console.warn('Auto-play audio failed:', error); + }); + }, 800); + + return () => { + clearTimeout(timer); + stopAllAudio(); + }; + } + }, [currentQuestionIndex, currentQuestion, showFeedback, mode, selectedLanguage, isPreview, stopAllAudio, effectiveLanguage]); + + /** + * Effect: Play success audio when correct answer is shown + * + * Plays success sound immediately when correct answer is selected + * Note: In preview mode, success sound is handled by the preview component, so we skip it here + * + * In game mode: Uses audio instruction language for success feedback + * In preview mode: Uses effectiveLanguage (for preview compatibility) + */ + useLayoutEffect(() => { + if (showFeedback) { + setIsFeedbackAudioPlaying(true); + } + }, [showFeedback]); + + useEffect(() => { + if (showFeedback && isCorrect && currentQuestion && selectedAnswer && selectedLanguage && !isPreview) { + let isActive = true; + + const playSuccessAudio = async () => { + setIsFeedbackAudioPlaying(true); + try { + // In game mode, use audio instruction language for success feedback + const successLanguage = getFeedbackLanguage(); + await playSuccessSound(successLanguage, { exactLanguage: true }); + } catch (error) { + console.warn('Success audio playback failed:', error); + } finally { + if (isActive) { + setIsFeedbackAudioPlaying(false); + } + } + }; + + playSuccessAudio(); + + return () => { + isActive = false; + }; + } + }, [showFeedback, isCorrect, currentQuestion, selectedAnswer, selectedLanguage, isPreview, getFeedbackLanguage]); + useEffect(() => { + if (!showFeedback) { + setIsFeedbackAudioPlaying(false); + } + }, [showFeedback]); + + + /** + * Effect: Play feedback audio when incorrect answer is shown + * + * In preview mode: Waits 2 seconds to allow failure sound (from preview component) to complete first + * In game mode: Starts immediately after feedback UI is shown + */ + useEffect(() => { + if (showFeedback && !isCorrect && currentQuestion && selectedAnswer && selectedLanguage) { + // In preview mode, wait longer to allow failure sound to complete first + // Failure sound typically takes 1-2 seconds, so we wait 2 seconds to be safe + // In game mode, start immediately + const delay = isPreview ? 2000 : 500; + + const timer = setTimeout(() => { + playFeedbackAudio(); + }, delay); + + // Cleanup: stop audio if component unmounts or effect re-runs + return () => { + clearTimeout(timer); + stopAllAudio(); + }; + } + }, [showFeedback, isCorrect, currentQuestion, selectedAnswer, selectedLanguage, playFeedbackAudio, stopAllAudio, isPreview]); + + // Cleanup audio on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, [stopAllAudio]); + + // Don't render if no current question + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + const Container: any = useContainer === 'card' ? Card : 'div'; + const containerClass = useContainer === 'card' + ? `flex-1 p-0 sm:p-0.5 md:p-1 lg:p-2 bg-white/95 backdrop-blur-sm shadow-floating overflow-y-auto flex flex-col relative ${className}` + : `flex-1 p-0 sm:p-0.5 md:p-1 lg:p-2 overflow-y-auto flex flex-col relative ${className}`; + + return ( + + {/* Progress Bar with Stars and Lives */} + {showProgress && progress && ( +
+ +
+ )} + + {/* Game Area */} +
+ {/* Top Section - Audio */} + {showSpeaker && ( +
+
+ 🔊 +
+ + {/* Hand pointer for preview mode */} + {mode === 'preview' && demoStep === 'waitForSpeaker' && !hasClickedSpeaker && ( +
+ 👆 +
+ )} +
+ )} + + {/* Middle Section - Letter Options */} +
+
+ {/* Hand Icon - positioned to the left of options section */} + {mode === 'preview' && showHandPointer && !showFeedback && ( +
+
+ 👆 +
+
+ )} + + {/* Options Grid */} +
+ {currentQuestion.options.map((letter, index) => { + const isSelected = letter === selectedAnswer; + const isGreyedOut = selectedAnswer !== null && !isSelected; + + return ( + + ); + })} +
+
+
+ + {/* Bottom Section - Feedback */} +
+
+ {showFeedback && ( +
+ {isCorrect ? ( +
+

+ {selectedLanguage === 'te' + ? '🎉 సరైనది!' + : selectedLanguage === 'kn' + ? '🎉 ಸರಿಯಿದೆ!' + : selectedLanguage === 'mr' + ? '🎉 बरोबर!' + : selectedLanguage === 'hi' + ? '🎉 सही है।' + : '🎉 Correct!'} +

+
+ ) : ( +
+

+ {(() => { + // Use heart break emoji if game uses hearts/lives system + const emoji = (maxLives && maxLives > 0) ? '💔' : '😢'; + return selectedLanguage === 'te' + ? `${emoji} అయ్యో! తప్పు!` + : selectedLanguage === 'kn' + ? `${emoji} ಅಯ್ಯೋ! ತಪ್ಪು!` + : selectedLanguage === 'mr' + ? `${emoji} अरेच्या! चुकीचे!` + : selectedLanguage === 'hi' + ? `${emoji} ओह! गलत!` + : `${emoji} Oops! Wrong!`; + })()} +

+
+ )} + + {/* Continue Button */} + +
+ )} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/LetterLauncherGame.tsx b/src/lib/axl-explorations/src/components/games/LetterLauncherGame.tsx new file mode 100644 index 00000000..e1d50384 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/LetterLauncherGame.tsx @@ -0,0 +1,1292 @@ +import { useState, useEffect, useCallback, useRef } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { FuelProgressBar } from "../FuelProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LetterLauncherLevelSelector } from "../LetterLauncherLevelSelector"; +import { SpaceBackground } from "../SpaceBackground"; +import { PlanetWithRocketAnimation } from "../PlanetWithRocketAnimation"; +import { TryAgain } from "../TryAgain"; +import { PlanetIcon } from "../ui/PlanetIcon"; +import { LetterLauncherGameCore, type LetterLauncherQuestion } from "./LetterLauncherGameCore"; +import { ArrowLeft, RotateCcw, TrendingUp, Rocket, Fuel, Lock as LockIcon } from "lucide-react"; +import { CountdownTimer } from "../CountdownTimer"; +import { LetterLauncherGameStoryPreview } from "./LetterLauncherGameStoryPreview"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { memoryGameDataLoader } from "../../utils/memoryGameDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { playLetterAudio } from "../../utils/letterAudioUtils"; +import { playSuccessSound } from "../../utils/audioUtils"; +import { calculateFuel, getFuelRequirement, getMissionDestination, FuelCalculationResult } from "../../utils/fuelCalculation"; + +interface LetterLauncherGameProps { + onBack: () => void; + contentCount?: number; // Optional: number of questions per level (default: 30) +} + +export function LetterLauncherGame({ onBack, contentCount = 30 }: LetterLauncherGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); + const [showPreview, setShowPreview] = useState(true); // Show preview/countdown when first opening game + const [showStoryPreview, setShowStoryPreview] = useState(false); // Show story preview after countdown + const [showMissionBriefing, setShowMissionBriefing] = useState(false); + const [showCountdown, setShowCountdown] = useState(false); + const [gameSessionStarted, setGameSessionStarted] = useState(false); // Track if game session has actually started + const [isStartingSession, setIsStartingSession] = useState(false); // Prevent double-start + const [levelFuelStats, setLevelFuelStats] = useState>(new Map()); + const [isLoadingFuelStats, setIsLoadingFuelStats] = useState(true); + const [levelUnlockStatus, setLevelUnlockStatus] = useState>(new Map()); + const levelUnlockStatusRef = useRef>(new Map()); + + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + + // Fuel mechanics + const [currentFuel, setCurrentFuel] = useState(0); + const [displayedFuel, setDisplayedFuel] = useState(0); // Fuel displayed in progress bar (updates only after continue) + const [fuelEarned, setFuelEarned] = useState(null); + const [showLetter, setShowLetter] = useState(false); // Control when letter appears (after audio) + const [isPlayingAudio, setIsPlayingAudio] = useState(false); + const audioAbortRef = useRef(false); // Ref to abort audio playback + + // Adaptive timer + const [adaptiveTimer, setAdaptiveTimer] = useState(3000); // Start at 3 seconds + const [responseTimeHistory, setResponseTimeHistory] = useState([]); + + // Telemetry state - use ref for questionStartTime to avoid React state update delays + const questionStartTimeRef = useRef(0); + const [audioStartTime, setAudioStartTime] = useState(0); + // Use ref to track currentFuel to avoid stale closure values in handleContinue + const currentFuelRef = useRef(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations - 10 levels + const getLanguageLevels = (language: Language) => { + return { + maxLevels: 10, + levelNames: ['Moon', 'Mars', 'Jupiter', 'Saturn', 'Venus', 'Uranus', 'Neptune', 'Mercury', 'Pluto', 'Sun'] + }; + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `letterLauncher_${selectedLanguage}` : 'letterLauncher'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + const { requiredFuel, maxFuel } = getFuelRequirement(currentLevel, contentCount); + const missionDestination = getMissionDestination(currentLevel); + + // Letter Launcher level to Letter Hunt levels mapping (configurable) + // Each Letter Launcher level uses letters from specific Letter Hunt levels + const LETTER_LAUNCHER_LEVEL_CONFIG: Record = { + 1: [1, 2, 3], // Moon - uses Letter Hunt L-1, L-2, L-3 + 2: [4, 5, 6], // Mars - uses Letter Hunt L-4, L-5, L-6 + 3: [7, 8, 9, 10], // Jupiter - uses Letter Hunt L-7, L-8, L-9, L-10 + 4: [1, 2, 3], // Saturn - uses Letter Hunt L-1, L-2, L-3 + 5: [4, 5, 6], // Venus - uses Letter Hunt L-4, L-5, L-6 + 6: [7, 8, 9, 10], // Uranus - uses Letter Hunt L-7, L-8, L-9, L-10 + 7: [1, 2, 3], // Neptune - uses Letter Hunt L-1, L-2, L-3 + 8: [4, 5, 6], // Mercury - uses Letter Hunt L-4, L-5, L-6 + 9: [7, 8, 9, 10], // Pluto - uses Letter Hunt L-7, L-8, L-9, L-10 + 10: [10] // Sun - only L-10 (most infrequent letters) + }; + + // Get level-appropriate letters + const getLevelLetters = (language: Language, level: number): string[] => { + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // Get the Letter Hunt levels to use for this Letter Launcher level + const letterHuntLevels = LETTER_LAUNCHER_LEVEL_CONFIG[level] || [1, 2, 3]; + + // For Telugu, Kannada, and Marathi, use levelLetters from multiple Letter Hunt levels + if (supportedLanguage === 'te' || supportedLanguage === 'kn' || supportedLanguage === 'mr') { + const allLetters: string[] = []; + letterHuntLevels.forEach(huntLevel => { + const letters = memoryGameDataLoader.getLettersByLevel(supportedLanguage, huntLevel.toString()); + allLetters.push(...letters); + }); + // Remove duplicates + return [...new Set(allLetters)]; + } + + // For English, map Letter Hunt levels to complexity + // L-1,2,3 -> basic, L-4,5,6 -> intermediate, L-7,8,9,10 -> advanced/expert/master + const allLetters: string[] = []; + letterHuntLevels.forEach(huntLevel => { + let complexity: string; + if (huntLevel <= 3) { + complexity = 'basic'; + } else if (huntLevel <= 6) { + complexity = 'intermediate'; + } else if (huntLevel <= 8) { + complexity = 'advanced'; + } else if (huntLevel <= 9) { + complexity = 'expert'; + } else { + complexity = 'master'; + } + const letters = memoryGameDataLoader.getLetters(supportedLanguage, complexity); + allLetters.push(...letters); + }); + // Remove duplicates + return [...new Set(allLetters)]; + }; + + // Generate questions (configurable count per level, default: 30) + const generateQuestions = (language: Language, level: number, complexity: string, count: number = contentCount): LetterLauncherQuestion[] => { + const lettersToUse = getLevelLetters(language, level); + const questions: LetterLauncherQuestion[] = []; + + if (lettersToUse.length === 0) { + console.warn(`No letters available for ${language} level ${level}`); + return []; + } + + // Generate questions: 50% match, 50% non-match + for (let i = 0; i < count; i++) { + const audioLetter = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + const isMatch = Math.random() < 0.5; // 50% chance of match + + let displayedLetter: string; + if (isMatch) { + displayedLetter = audioLetter; // Match case + } else { + // Non-match: pick a different letter + const otherLetters = lettersToUse.filter(l => l !== audioLetter); + displayedLetter = otherLetters.length > 0 + ? otherLetters[Math.floor(Math.random() * otherLetters.length)] + : audioLetter; // Fallback if only one letter available + } + + questions.push({ + audioLetter, + displayedLetter, + isMatch, + complexity, + language + }); + } + + return questions; + }; + + // Calculate adaptive timer based on past performance + const calculateAdaptiveTimer = async (language: Language, level: number): Promise => { + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + return 3000; // Default 3 seconds + } + + try { + // Search for past performance data + const gameName = gameKey.split('_')[0]; + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: language, + contentId: `level${level}` + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + if (result.success && result.data) { + // Extract response times from assessment summaries + const allResponseTimes: number[] = []; + + // Iterate through level data + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelData = (result.data as any)[levelKey]; + const assessmentSummary = levelData?.assessmentSummary || []; + + assessmentSummary.forEach((section: any) => { + if (section.data) { + section.data.forEach((item: any) => { + if (item.duration && item.pass === 'Yes') { + allResponseTimes.push(item.duration); + } + }); + } + }); + }); + + if (allResponseTimes.length > 0) { + // Calculate average response time + const avgResponseTime = allResponseTimes.reduce((a, b) => a + b, 0) / allResponseTimes.length; + // Set timer to average + 20% buffer, but minimum 2 seconds, maximum 5 seconds + const calculatedTimer = Math.max(2000, Math.min(5000, avgResponseTime * 1.2)); + return calculatedTimer; + } + } + } catch (error) { + console.error('Error calculating adaptive timer:', error); + } + + return 3000; // Default 3 seconds + }; + + + // Initialize game session and questions when language or level is selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + console.log(`[LetterLauncher] Initialization useEffect running for level ${selectedLevel}, language ${selectedLanguage}`); + + // Abort any ongoing audio playback + audioAbortRef.current = true; + + // Stop any playing audio first + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + const audioElements = document.querySelectorAll('audio'); + audioElements.forEach(audio => { + audio.pause(); + audio.currentTime = 0; + }); + + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment - but DON'T set levelStartTime here + // levelStartTime will be set when "Start Mission" is clicked to match telemetry timing + questionStartTimeRef.current = 0; // Reset question start time + setQuestionSummaries([]); + + // DON'T start telemetry subsession here - it will start when "Start Mission" is clicked + // This prevents the sub-session from starting when mission briefing is shown + + // When selecting a level, always go to mission briefing (no countdown) + setShowMissionBriefing(true); + setShowCountdown(false); + // Reset game session started flag - audio should not play until "Start Mission" is clicked + setGameSessionStarted(false); + // Keep abort flag set - it will be cleared when "Start Mission" is clicked + + // Calculate adaptive timer + const timer = await calculateAdaptiveTimer(selectedLanguage, currentLevel); + setAdaptiveTimer(timer); + console.log(`Adaptive timer set to ${timer}ms`); + + // Generate questions (use contentCount prop, default: 30) + const newQuestions = generateQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + contentCount + ); + setQuestions(newQuestions); + setCurrentFuel(0); + currentFuelRef.current = 0; // Sync ref with state + setDisplayedFuel(0); + setCurrentQuestionIndex(0); + setShowLetter(false); + setIsPlayingAudio(false); + // Reset feedback state when switching levels + setShowFeedback(false); + setSelectedAnswer(null); + setIsCorrect(false); + setFuelEarned(null); + // Reset game completion states when switching levels + setIsGameComplete(false); + setLevelFailed(false); + setShowLevelUp(false); + setTotalCorrect(0); + console.log(`Generated ${newQuestions.length} questions for level ${currentLevel}`); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, isGameComplete]); // Removed gameKey from dependencies to prevent double-run + + // Reset game state when navigating to a new level via URL (similar to other games) + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setIsGameComplete(false); + setLevelFailed(false); + setShowLevelUp(false); + setCurrentQuestionIndex(0); + setTotalCorrect(0); + setCurrentFuel(0); + currentFuelRef.current = 0; // Sync ref with state + setDisplayedFuel(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrect(false); + setFuelEarned(null); + setShowLetter(false); + setIsPlayingAudio(false); + setShowMissionBriefing(true); + setShowCountdown(false); + setGameSessionStarted(false); + setResponseTimeHistory([]); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + } + }, [selectedLevel]); + + // Handle mission briefing continue -> game start + const handleMissionBriefingContinue = async () => { + setShowMissionBriefing(false); + await startGameSession(); + }; + + const handleCountdownComplete = async () => { + console.log('[LetterLauncher] Countdown complete, showing story preview'); + setShowCountdown(false); + // After countdown, show story preview + setShowStoryPreview(true); + // Also set showPreview to false to prevent countdown from showing again + setShowPreview(false); + }; + + const handleStoryPreviewComplete = () => { + setShowStoryPreview(false); + // After story preview, show level selector + setShowPreview(false); + }; + + const startGameSession = async () => { + // Prevent double-start + if (isStartingSession) { + console.log(`[LetterLauncher] startGameSession already in progress, skipping`); + return; + } + + setIsStartingSession(true); + + try { + // Start telemetry subsession when "Start Mission" is clicked (not when mission briefing is shown) + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage!); + + // Set levelStartTime NOW (when gameplay actually starts) to match telemetry timing + // This ensures tracking service and telemetry calculate duration from the same start point + const now = Date.now(); + setLevelStartTime(now); + questionStartTimeRef.current = now; + + // Clear abort flag and mark that game session has started (allows audio to play) + audioAbortRef.current = false; + setGameSessionStarted(true); + } finally { + setIsStartingSession(false); + } + }; + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + const gameName = gameKey.split('_')[0]; + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + if (result.success && result.data && typeof result.data === 'object') { + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Store unlock status for all levels + const unlockMap = new Map(); + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + const isUnlocked = levelData?.metadata?.isUnlocked ?? (levelNumber === 1); + unlockMap.set(levelNumber, isUnlocked); + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + setLevelUnlockStatus(unlockMap); + levelUnlockStatusRef.current = unlockMap; + + const computedFromProgress = Math.min( + Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), + languageLevels.maxLevels + ); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min( + Math.max(computedFromProgress, backendProvided), + languageLevels.maxLevels + ); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Stop audio when mission briefing is shown + useEffect(() => { + if (showMissionBriefing) { + // Abort any ongoing audio playback + audioAbortRef.current = true; + + // Stop any playing audio when mission briefing is shown + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + const audioElements = document.querySelectorAll('audio'); + audioElements.forEach(audio => { + audio.pause(); + audio.currentTime = 0; + }); + setIsPlayingAudio(false); + // Keep abort flag set - it will be cleared when "Start Mission" is clicked + } + }, [showMissionBriefing]); + + // Play audio and show letter after audio ends - Only after game session has started + useEffect(() => { + // Don't play audio if mission briefing is shown or game session hasn't started + if (!currentQuestion || showFeedback || isGameComplete || !selectedLanguage || selectedLevel === null || showCountdown || showMissionBriefing || !gameSessionStarted || audioAbortRef.current) { + // Stop any playing audio if conditions are not met + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + const audioElements = document.querySelectorAll('audio'); + audioElements.forEach(audio => { + audio.pause(); + audio.currentTime = 0; + }); + setIsPlayingAudio(false); + return; + } + + const playQuestionAudio = async () => { + // Check if audio was aborted before starting + if (audioAbortRef.current) { + return; + } + + setShowLetter(false); + setIsPlayingAudio(true); + const audioStart = Date.now(); + setAudioStartTime(audioStart); + + // Play audio + await playLetterAudio(currentQuestion.audioLetter, selectedLanguage); + + // Check again if audio was aborted during playback + if (audioAbortRef.current) { + setIsPlayingAudio(false); + return; + } + + // Set question start time BEFORE showing the letter to ensure accurate timing + // This is the moment the user can start responding + // Use ref to ensure immediate access without React state update delay + const letterAppearTime = Date.now(); + questionStartTimeRef.current = letterAppearTime; + + // After audio ends, show the letter + setShowLetter(true); + setIsPlayingAudio(false); + + // Debug log to verify timing + console.log('[LetterLauncher] Letter appeared, questionStartTimeRef set to:', letterAppearTime); + }; + + playQuestionAudio(); + + // Cleanup: stop audio if component unmounts or conditions change + return () => { + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + const audioElements = document.querySelectorAll('audio'); + audioElements.forEach(audio => { + audio.pause(); + audio.currentTime = 0; + }); + setIsPlayingAudio(false); + }; + }, [currentQuestionIndex, currentQuestion, showFeedback, isGameComplete, selectedLanguage, selectedLevel, showCountdown, showMissionBriefing, gameSessionStarted]); + + // Handle keyboard input (Left/W = Match, Right/M = Non-match) + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + if (showFeedback || isGameComplete || !currentQuestion || !showLetter || isPlayingAudio) return; + + if (event.key === 'ArrowLeft' || event.key === 'w' || event.key === 'W') { + event.preventDefault(); + handleAnswerSelect(true); // Match + } else if (event.key === 'ArrowRight' || event.key === 'm' || event.key === 'M') { + event.preventDefault(); + handleAnswerSelect(false); // Non-match + } + }; + + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + }, [showFeedback, isGameComplete, currentQuestion, showLetter, isPlayingAudio]); + + // Fetch fuel stats for levels when showing level selector + useEffect(() => { + const fetchFuelStats = async () => { + if (!showLevelSelector || !selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingFuelStats(false); + return; + } + + try { + setIsLoadingFuelStats(true); + const gameName = gameKey.split('_')[0]; + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + const fuelMap = new Map(); + + if (result.success && result.data && typeof result.data === 'object') { + for (let level = 1; level <= 2; level++) { + const levelKey = `level${level}`; + const levelData = (result.data as any)[levelKey]; + if (levelData?.metadata?.fuelEarned) { + fuelMap.set(level, levelData.metadata.fuelEarned); + } + } + } + + setLevelFuelStats(fuelMap); + } catch (error) { + console.error('Error fetching fuel stats:', error); + } finally { + setIsLoadingFuelStats(false); + } + }; + + fetchFuelStats(); + }, [showLevelSelector, selectedLanguage, gameKey]); + + const handleAnswerSelect = async (isMatch: boolean) => { + if (showFeedback || !currentQuestion || !showLetter) return; + + // IMMEDIATELY capture response time before any async operations + const now = Date.now(); + const questionStart = questionStartTimeRef.current; + const responseTime = questionStart > 0 ? now - questionStart : 0; + + setSelectedAnswer(isMatch); + const correct = isMatch === currentQuestion.isMatch; + setIsCorrect(correct); + setShowFeedback(true); + + // Calculate fuel earned IMMEDIATELY (before any async operations) + const fuelResult = calculateFuel(responseTime, correct); + setFuelEarned(fuelResult); + + // Debug logging for fuel calculation + console.log('[LetterLauncher] Fuel Calculation:', { + questionStartTime: questionStart, + currentTime: now, + responseTime: `${responseTime}ms`, + isCorrect: correct, + fuelEarned: fuelResult.fuelEarned, + speedTier: fuelResult.speedTier, + currentFuel: currentFuel, + newFuel: correct ? currentFuel + fuelResult.fuelEarned : currentFuel + }); + + // Play success sound for correct answers AFTER calculating fuel (non-blocking) + // Use audio instruction language (not learning language) + if (correct) { + const audioLang = selectedAudioLanguage || selectedLanguage || 'en'; + playSuccessSound(audioLang, { exactLanguage: true }).catch(error => { + console.warn('Success audio playback failed:', error); + }); + } + + if (correct) { + setCurrentFuel(prev => { + const newFuel = prev + fuelResult.fuelEarned; + console.log(`Fuel updated: ${prev} + ${fuelResult.fuelEarned} = ${newFuel}`); + // Update ref to keep it in sync with state + currentFuelRef.current = newFuel; + // Update telemetry subsession with current fuel (for page refresh case) + sessionTelemetryManager.updateSubSessionFuel(newFuel); + return newFuel; + }); + setTotalCorrect(prev => prev + 1); + // Track response time for adaptive timer + setResponseTimeHistory(prev => [...prev, responseTime]); + } else { + console.log('Incorrect answer - no fuel earned'); + } + + recordAnswer(correct); + + // Telemetry assess - pass fuel points for variable scoring (0, 1, 3, or 5) + const questionId = `letterLauncher_${currentLevel}_${currentQuestionIndex}`; + + // Format user answer as colon-separated string: "audioLetter:displayedLetter:userSelected" + // Example: "A:A:true" (user selected match) or "A:B:false" (user selected non-match) + const userAnswer = `${currentQuestion.audioLetter}:${currentQuestion.displayedLetter}:${isMatch ? 'true' : 'false'}`; + + // Format correct answer as colon-separated string: "audioLetter:displayedLetter:isMatch" + // Example: "A:A:true" (is a match) or "A:B:false" (is not a match) + const correctAnswer = `${currentQuestion.audioLetter}:${currentQuestion.displayedLetter}:${currentQuestion.isMatch}`; + + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'letterLauncher', + userAnswer, + correctAnswer, + correct, + responseTime, + fuelResult.fuelEarned, // Pass fuel points for variable scoring in telemetry + 5 // Max score per question (maximum fuel points) + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'letterLauncher', + userAnswer: userAnswer, + correctAnswer: correctAnswer, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity, + points: fuelResult.fuelEarned // Store fuel earned as points for tracking + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + }; + + const handleContinue = useCallback(async () => { + // Update displayed fuel when continue is clicked (after answer is confirmed) + // Use ref to get the latest fuel value to avoid stale closure issues + const latestFuel = currentFuelRef.current; + setDisplayedFuel(latestFuel); + + if (currentQuestionIndex < questions.length - 1) { + // IMPORTANT: Reset showLetter to false BEFORE changing question index + // This ensures the audio icon shows first, then letter appears after audio + setShowLetter(false); + setCurrentQuestionIndex(currentQuestionIndex + 1); + setShowFeedback(false); + setSelectedAnswer(null); + setFuelEarned(null); + } else { + // Level complete - check fuel requirement + // IMPORTANT: Use ref to get latest fuel value to avoid stale closure issues + // currentFuelRef.current already includes the last question's fuel (updated in handleAnswerSelect) + // So we should NOT add fuelEarned again - that would double count! + const finalFuel = currentFuelRef.current; // Use ref to get latest value including last question + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const hasEnoughFuel = finalFuel >= requiredFuel; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + // Build complete summaries array including last question + const lastQuestionId = `letterLauncher_${currentLevel}_${currentQuestionIndex}`; + const hasLastQuestion = questionSummaries.some(q => q.questionId === lastQuestionId); + let allSummariesForTracking = [...questionSummaries]; + + if (!hasLastQuestion && fuelEarned && currentQuestion) { + // Format user answer as colon-separated string: "audioLetter:displayedLetter:userSelected" + // Example: "A:A:true" (user selected match) or "A:B:false" (user selected non-match) or "A:A:unknown" + const lastUserAnswer = selectedAnswer !== null + ? `${currentQuestion.audioLetter}:${currentQuestion.displayedLetter}:${selectedAnswer ? 'true' : 'false'}` + : `${currentQuestion.audioLetter}:${currentQuestion.displayedLetter}:unknown`; + + // Format correct answer as colon-separated string: "audioLetter:displayedLetter:isMatch" + // Example: "A:A:true" or "A:B:false" + const lastCorrectAnswer = `${currentQuestion.audioLetter}:${currentQuestion.displayedLetter}:${currentQuestion.isMatch}`; + + const lastQuestionSummary: QuestionSummary = { + questionId: lastQuestionId, + questionType: 'letterLauncher', + userAnswer: lastUserAnswer, + correctAnswer: lastCorrectAnswer, + isCorrect: isCorrect, + responseTime: 0, + complexity: currentQuestion.complexity, + points: fuelEarned.fuelEarned + }; + allSummariesForTracking = [...questionSummaries, lastQuestionSummary]; + } + + // Update state with complete summaries + setQuestionSummaries(allSummariesForTracking); + + // Await the assessment tracking to ensure backend processes completion before refreshing unlock status + const actualCorrect = allSummariesForTracking.filter(q => q.isCorrect).length; + const totalScoreFromFuel = currentFuelRef.current; + + await trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Letter Launcher', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: totalScoreFromFuel, + timeSpent: totalTimeSpent, + assessmentSummary: allSummariesForTracking, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: !hasEnoughFuel, + scorePercentage: (actualCorrect / questions.length) * 100, + fuelEarned: finalFuel, + fuelRequired: requiredFuel, + missionDestination: missionDestination + } + }); + } + + // End telemetry subsession - pass total fuel earned and required fuel for Letter Launcher + const finalFuelForTelemetry = currentFuelRef.current; // Use ref to get latest fuel value + await sessionTelemetryManager.endSubSession(finalFuelForTelemetry, requiredFuel); + await sessionTelemetryManager.flushAssessEventBatch(); + + if (hasEnoughFuel) { + console.log(`Letter Launcher completed for ${selectedLanguage}, previous level: ${previousLevel}`); + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + setLevelFailed(false); + + // Refresh unlock status after level completion to get updated backend data + // IMPORTANT: Await this before setting isGameComplete to ensure success screen has correct unlock status + // Also add small delay to ensure backend has processed the completion + if (currentUser) { + try { + // Small delay to ensure backend has processed the completion + await new Promise(resolve => setTimeout(resolve, 1000)); + + const gameName = gameKey.split('_')[0]; + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + if (result.success && result.data && typeof result.data === 'object') { + const unlockMap = new Map(); + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const isUnlocked = levelData?.metadata?.isUnlocked ?? (levelNumber === 1); + unlockMap.set(levelNumber, isUnlocked); + }); + // Update both state and ref - ref is immediately available for success screen + setLevelUnlockStatus(unlockMap); + levelUnlockStatusRef.current = unlockMap; + } + } catch (error) { + console.error('Error refreshing unlock status:', error); + } + } + } else { + setLevelFailed(true); + } + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey, currentFuel, fuelEarned, requiredFuel, missionDestination, currentLevel, selectedLanguage, currentQuestion, selectedAnswer, difficultySettings.complexity]); + + // Auto-advance to next question after feedback is shown + useEffect(() => { + if (!showFeedback || isGameComplete || !currentQuestion) return; + + // Auto-advance after 2 seconds (2000ms) to show feedback + const autoAdvanceTimer = setTimeout(async () => { + await handleContinue(); + }, 2000); + + // Cleanup timer if component unmounts or feedback changes + return () => { + clearTimeout(autoAdvanceTimer); + }; + }, [showFeedback, isGameComplete, currentQuestion, handleContinue]); + + const handleBackClick = async () => { + // Abort any ongoing audio playback + audioAbortRef.current = true; + + // Stop any playing audio + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + // Stop any HTML audio elements + const audioElements = document.querySelectorAll('audio'); + audioElements.forEach(audio => { + audio.pause(); + audio.currentTime = 0; + }); + + // Reset game session started flag + setGameSessionStarted(false); + + // End telemetry subsession - pass total fuel earned and required fuel for Letter Launcher (if available) + const currentFuelForTelemetry = currentFuelRef.current; // Use ref to get latest fuel value + await sessionTelemetryManager.endSubSessionWithBackButton(currentFuelForTelemetry, requiredFuel); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrect(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setCurrentFuel(0); + currentFuelRef.current = 0; // Sync ref with state + setDisplayedFuel(0); + setFuelEarned(null); + setShowLetter(false); + setIsPlayingAudio(false); + setResponseTimeHistory([]); + setShowMissionBriefing(true); + setShowCountdown(false); + setGameSessionStarted(false); + setIsStartingSession(false); // Reset the starting flag + audioAbortRef.current = true; + + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + contentCount + ); + setQuestions(newQuestions); + } + }; + + const handleLevelSelect = (level: number) => { + navigate(`/letter-launcher-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/letter-launcher-game'); + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Speed Master - Perfect Score!"); + } + if (currentFuel >= requiredFuel) { + achievements.push(`Mission Success! Reached ${missionDestination}!`); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( + +
Loading...
+
+ ); + } + + // Show story preview after countdown (check this first) + if (showStoryPreview && selectedLanguage) { + console.log('[LetterLauncher] Rendering story preview, level:', backendCurrentLevel); + return ( + { + setShowStoryPreview(false); + setShowPreview(false); + onBack(); + }} + level={backendCurrentLevel} + hideHeader={false} + /> + ); + } + + // Show countdown when first opening game (before level selector) + // Only show if: (backend level is 1 AND level 1 has no progress) - same as other games + const shouldShowCountdown = showPreview && selectedLanguage && !showStoryPreview && + ((backendCurrentLevel === 1 && !level1HasProgress)); + + if (shouldShowCountdown) { + return ( +
+
+
+ +
+ +
+
+ ); + } + + // Show mission briefing screen - Visual and child-friendly + if (showMissionBriefing && selectedLanguage && selectedLevel !== null) { + return ( + +
+
+ +
+ + +
+ {/* Visual Elements */} +
+ {/* Moon with orbiting rocket */} +
+ +
+ + {/* Mission Title */} +

+ Reach {missionDestination}! +

+
+ + {/* Fuel Requirement */} +
+
+ +
+
+ {requiredFuel} +
+
+ Fuel Needed +
+
+
+ + {/* Quick instruction */} +

+ Answer fast = More fuel! 🚀 +

+
+ + {/* Start Button */} +
+ +
+
+
+
+
+ ); + } + + + // Show level selection screen - Use custom LetterLauncherLevelSelector + if (showLevelSelector && !shouldShowCountdown) { + const levelSelectorCurrentLevel = gameProgress.currentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + // Replay the story preview demo + setShowStoryPreview(true); + setShowPreview(false); + }} + gameKey={gameKey} + /> + ); + } + + // Show success/failure screen when game is complete + if (isGameComplete) { + // IMPORTANT: Use ref to get latest fuel value to avoid stale closure issues + // currentFuelRef.current already includes the last question's fuel (updated in handleAnswerSelect) + // So we should NOT add fuelEarned again - that would double count! + const finalFuel = currentFuelRef.current; // Use ref to get latest value including last question + + // Calculate stars based on fuel percentage + // const fuelPercentage = (finalFuel / requiredFuel) * 100; + const starsEarned = (finalFuel < requiredFuel) ? 1 : (finalFuel - requiredFuel > (maxFuel - requiredFuel) / 2 ? 3 : 2); + + if (levelFailed) { + return ( + + ); + } + + return ( + { + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage}`); + manuallyAdvanceLevel(gameKey, nextLevel); + navigate(`/letter-launcher-game/level/${nextLevel}`); + }} + fuelMode={true} + fuelCollected={finalFuel} + fuelRequired={requiredFuel} + destination={missionDestination} + nextDestination={currentLevel < languageLevels.maxLevels ? getMissionDestination(currentLevel + 1) : ''} + useSpaceBackground={true} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( + +
Loading...
+
+ ); + } + + return ( + +
+ {/* Header */} +
+ + +
+

+ Letter Launcher +

+
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • Mission: {missionDestination} + +
+
+ + {/* Spacer to balance layout */} +
+
+ + {/* Main Content Card */} + + {/* Fuel Progress */} +
+ +
+ + {/* Game Area - transparent to show space background */} +
+
+ +
+
+
+
+
+ ); +} + +export default LetterLauncherGame; + diff --git a/src/lib/axl-explorations/src/components/games/LetterLauncherGameCore.tsx b/src/lib/axl-explorations/src/components/games/LetterLauncherGameCore.tsx new file mode 100644 index 00000000..35240648 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/LetterLauncherGameCore.tsx @@ -0,0 +1,358 @@ +import { Button } from "../ui/button"; +import { Check, X, Fuel, ArrowLeft, ArrowRight } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { FuelCalculationResult, getFuelTierText } from "../../utils/fuelCalculation"; +import { useState, useEffect } from "react"; +import React from "react"; + +export interface LetterLauncherQuestion { + audioLetter: string; // The letter sound that plays + displayedLetter: string; // The letter shown on screen + isMatch: boolean; // Whether displayed letter matches audio letter + complexity: string; + language: Language; +} + +export interface LetterLauncherGameCoreProps { + currentQuestion: LetterLauncherQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + showFeedback?: boolean; + isCorrect?: boolean; + selectedAnswer?: boolean | null; + fuelEarned?: FuelCalculationResult | null; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onAnswerSelect: (isMatch: boolean) => void; + onContinue?: () => void; + className?: string; + fuelIconImage?: string; // Optional custom fuel icon image path +} + +export function LetterLauncherGameCore({ + currentQuestion, + mode, + selectedLanguage, + showFeedback = false, + isCorrect = false, + selectedAnswer = null, + fuelEarned = null, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onAnswerSelect, + onContinue, + className = '', + fuelIconImage +}: LetterLauncherGameCoreProps) { + // State for fuel fill animation + const [fillWidth, setFillWidth] = useState(0); + const [sliderPosition, setSliderPosition] = useState(0); + + // Trigger fill animation when fuelEarned changes + useEffect(() => { + if (fuelEarned && isCorrect) { + // Calculate fill percentage (max 5 fuel = 100%, but cap at 100%) + const fillPercent = Math.min((fuelEarned.fuelEarned / 5) * 100, 100); + // Reset to 0 first, then animate to target + setFillWidth(0); + setSliderPosition(0); + // Small delay to ensure reset is visible, then animate + setTimeout(() => { + setFillWidth(fillPercent); + // Calculate slider position accounting for icon width + // Icon is 40px (w-10) on larger screens, so we need to account for half of that + // For 100%, position at ~92% to ensure icon stays fully within bar + // For other values, position at the fill percentage but ensure it doesn't exceed safe limit + const iconWidthPercent = 8; // Approximate percentage the icon takes (40px out of ~250px max width) + const sliderPos = fillPercent >= 100 + ? Math.max(0, 100 - iconWidthPercent) + : Math.min(fillPercent, 100 - iconWidthPercent); + setSliderPosition(sliderPos); + }, 50); + } else { + setFillWidth(0); + setSliderPosition(0); + } + }, [fuelEarned, isCorrect]); + + // Keyboard navigation: Arrow keys to select options + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + // Only handle arrow keys when buttons are enabled + if (disabled || showFeedback) { + return; + } + + // Prevent default behavior for arrow keys + if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + event.preventDefault(); + } + + // Left arrow = Yes (Check) = true + if (event.key === 'ArrowLeft') { + onAnswerSelect(true); + } + // Right arrow = No (X) = false + else if (event.key === 'ArrowRight') { + onAnswerSelect(false); + } + }; + + // Add event listener + window.addEventListener('keydown', handleKeyDown); + + // Cleanup + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [disabled, showFeedback, onAnswerSelect]); + + // Get localized text + const getLocalizedText = (key: string) => { + const texts = { + isThisMatch: { + en: 'Does this letter match the sound?', + te: 'ఈ అక్షరం ధ్వనికి సరిపోతుందా?', + kn: 'ಈ ಅಕ್ಷರವು ಧ್ವನಿಗೆ ಹೊಂದಿಕೆಯಾಗುತ್ತದೆಯೇ?', + mr: 'हा अक्षर आवाजाशी जुळतो का?' + }, + correctMessage: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿಯಿದೆ!', + mr: '🎉 बरोबर!' + }, + wrongMessage: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + + return texts[key as keyof typeof texts]?.[selectedLanguage] || texts[key as keyof typeof texts]?.en || ''; + }; + + return ( +
+
+ {/* Letter Display - Only show after audio ends */} +
+ {currentQuestion.displayedLetter ? ( +

+ {currentQuestion.displayedLetter} +

+ ) : ( +
+ 🔊 +
+ )} +
+ + {/* Action Buttons */} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+
+ + {/* Keyboard key indicator - Left Arrow */} + {isPreview && ( +
+
+ +
+
+ )} +
+ +
+ + {/* Keyboard key indicator - Right Arrow */} + {isPreview && ( +
+
+ +
+
+ )} +
+
+
+ + {/* Feedback Area - Fixed height container to prevent layout shift */} +
+ {showFeedback && ( +
+

+ {isCorrect ? getLocalizedText('correctMessage') : getLocalizedText('wrongMessage')} +

+ + {/* Fuel earned display with filling animation */} + {fuelEarned && isCorrect && ( +
+
+ {/* Fuel Icon and Amount */} +
+ {fuelIconImage ? ( + Fuel + ) : ( + + )} + + +{fuelEarned.fuelEarned} Fuel + +
+ + {/* Fuel Filling Progress Bar */} +
+
+ {/* Filling Animation */} +
+ {/* Animated shine effect */} +
+
+ {/* Fuel icon slider handle at the end of fill */} +
+
+ {/* Use fuel icon image if provided, otherwise fallback to Fuel component */} + {fuelIconImage ? ( + Fuel + ) : ( + + )} +
+
+
+
+
+
+ )} +
+ )} +
+
+
+ ); +} + +// Ensure the export is available +export default LetterLauncherGameCore; diff --git a/src/lib/axl-explorations/src/components/games/LetterLauncherGameStoryPreview.tsx b/src/lib/axl-explorations/src/components/games/LetterLauncherGameStoryPreview.tsx new file mode 100644 index 00000000..aec02326 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/LetterLauncherGameStoryPreview.tsx @@ -0,0 +1,2152 @@ +import { useState, useEffect, useRef, useCallback } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ReplayButton } from "../ui/ReplayButton"; +import { PlanetIcon } from "../ui/PlanetIcon"; +import { ResourceRequirementCard } from "../ui/ResourceRequirementCard"; +import { PlanetWithRocketAnimation } from "../PlanetWithRocketAnimation"; +import { ArrowLeft, Rocket, Fuel } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { playAudio, playTTS, playSuccessSound, stopAllAudio, attachSlowLoadToast } from "../../utils/audioUtils"; +import { LetterLauncherGameCore, type LetterLauncherQuestion } from "./LetterLauncherGameCore"; +import { getFuelRequirement, getMissionDestination } from "../../utils/fuelCalculation"; +import { playLetterAudio } from "../../utils/letterAudioUtils"; +import { memoryGameDataLoader } from "../../utils/memoryGameDataLoader"; +import { SpaceBackground } from "../SpaceBackground"; + +interface LetterLauncherGameStoryPreviewProps { + onStartGame: () => void; + onBack: () => void; + level?: number; + hideHeader?: boolean; +} + +type StoryPhase = + | 'intro' // Captain Rahi introduction + | 'riloAppears' // Rilo robot appears and explains mission + | 'practice1' // First practice round + | 'practice2' // Second practice round + | 'practice3' // Third practice round (optional) + | 'fuelExplanation' // Explain fuel system + | 'readyToStart'; // Final encouragement + +const storyScript = { + en: { + intro: { + narrator: "You are Captain Rahi, a brave little astronaut.", + rocket: "Your rocket is waiting on the launchpad, shining and humming quietly.", + destination: "Today, you're going on a space trip", + planet: "🌙" + }, + riloAppears: { + rilo: "Captain Rahi! Our rocket is ready to fly but it needs fuel!", + fuelRequirement: "We need fuel units to travel through the galaxy.", + fuelExplanation: "We can make fuel using your super listening ears!", + gameExplanation: "You'll hear a sound, you'll see a letter, and if they match, we get fuel for the rocket!", + rocket: "🚀" + }, + practice: { + intro: "Let's practice a tiny bit before our real trip!", + question: "Do they sound the same?", + controls: "You can choose in your own way!\n\nIf you think they match,\npress ← key.\n\nIf you think they do not match,\npress → key.\n\nOr you can just click the ✓ or ✗ buttons!\n\nWhatever feels easiest,\nyou're the Captain of the rocket!", + correctFeedback: "Yay! That helped us!", + wrongFeedback: "It's okay! Let's try again!", + greatJob: "Great job, Captain Rahi! You're ready!", + amazing: "You're doing amazing!", + allReady: "All ready!" + }, + fuelExplanation: { + rilo: "Everytime you listen and choose, your rocket gets more fuel! The faster you answer a question, the more fuel you get!", + fuelMeter: "Let's fill the tank, and explore the galaxy!!" + }, + readyToStart: { + rilo: "Are you ready to start your space trip, Captain?", + button: "🚀 Start Space Trip" + } + }, + te: { + intro: { + narrator: "మీరు కెప్టెన్ రాహి, ఒక ధైర్యవంతమైన చిన్న అంతరిక్ష యాత్రికుడు!", + rocket: "మీ రాకెట్ లాంచ్ ప్యాడ్‌లో వేచి ఉంది, మెరుస్తూ మరియు నిశ్శబ్దంగా గుణగుణిస్తూ.", + destination: "ఈరోజు, మీరు అంతరిక్ష ప్రయాణం చేస్తున్నారు!", + planet: "🌙" + }, + riloAppears: { + rilo: "కెప్టెన్ రాహి! మన రాకెట్ ఫ్లై చేయడానికి సిద్ధంగా ఉంది కానీ దానికి ఇంధనం కావాలి!", + fuelRequirement: "మనకు గెలాక్సీ ద్వారా ప్రయాణించడానికి ఇంధన యూనిట్‌లు కావాలి.", + fuelExplanation: "మీ సూపర్ వినే చెవులను ఉపయోగించి మనం ఇంధనాన్ని తయారు చేయవచ్చు!", + gameExplanation: "మీరు ఒక ధ్వనిని వింటారు, మీరు ఒక అక్షరాన్ని చూస్తారు, మరియు అవి సరిపోతే, మనకు రాకెట్‌కు ఇంధనం లభిస్తుంది!", + rocket: "🚀" + }, + practice: { + intro: "మన నిజమైన ప్రయాణానికి ముందు కొంచెం ప్రాక్టీస్ చేద్దాం!", + question: "అవి ఒకే ధ్వనిని ఇస్తాయా?", + controls: "మీరు మీ స్వంత మార్గంలో ఎంచుకోవచ్చు!\n\nమీరు అవి సరిపోతాయని అనుకుంటే,\n← కీ నొక్కండి.\n\nమీరు అవి సరిపోవు అని అనుకుంటే,\n→ కీ నొక్కండి.\n\nలేదా మీరు ✓ లేదా ✗ బటన్‌లను క్లిక్ చేయవచ్చు!\n\nఏది సులభంగా అనిపిస్తుందో,\nమీరు రాకెట్‌కు కెప్టెన్!", + correctFeedback: "యే! అది మాకు సహాయపడింది!", + wrongFeedback: "పరవాలేదు! మళ్లీ ప్రయత్నిద్దాం!", + greatJob: "చాలా బాగుంది, కెప్టెన్ రాహి! మీరు సిద్ధంగా ఉన్నారు!", + amazing: "మీరు అద్భుతంగా చేస్తున్నారు!", + allReady: "అన్నీ సిద్ధం!" + }, + fuelExplanation: { + rilo: "మీరు ప్రతిసారీ విని ఎంచుకున్నప్పుడు, మీ రాకెట్‌కు మరింత ఇంధనం లభిస్తుంది! మీరు ప్రశ్నకు వేగంగా సమాధానం ఇస్తే, మీకు ఎక్కువ ఇంధనం లభిస్తుంది!", + fuelMeter: "ట్యాంక్‌ను నింపుదాం, మరియు గెలాక్సీని అన్వేషిద్దాం!!" + }, + readyToStart: { + rilo: "మీరు మీ అంతరిక్ష ప్రయాణాన్ని ప్రారంభించడానికి సిద్ధంగా ఉన్నారా, కెప్టెన్?", + button: "🚀 అంతరిక్ష ప్రయాణం ప్రారంభించండి" + } + }, + kn: { + intro: { + narrator: "ನೀವು ಕ್ಯಾಪ್ಟನ್ ರಾಣಿ, ಸಾಹಸಿ ಹಾಗೂ ಯುವ ಬಾಹ್ಯಾಕಾಶ ಯಾತ್ರಿ.", + rocket: "ನಿಮ್ಮ ಹೊಳೆಯುವ ರಾಕೆಟ್ ಲಾಂಚ್ ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೌನವಾಗಿ ನಿಮಗಾಗಿ ಕಾಯುತ್ತಿದೆ.", + destination: "ಇಂದು, ನೀವು ಬಾಹ್ಯಾಕಾಶ ಪ್ರಯಾಣ ಮಾಡುತ್ತೀರಿ!", + planet: "🌙" + }, + riloAppears: { + rilo: "ಕ್ಯಾಪ್ಟನ್ ರಾಣಿ! ನಮ್ಮ ರಾಕೆಟ್ ಹಾರಲು ಸಿದ್ಧವಾಗಿದೆ, ಆದರೆ ಅದಕ್ಕೆ ಇಂಧನ ಬೇಕು!", + fuelRequirement: "ನಮಗೆ ಗೆಲಾಕ್ಸಿಯ ಮೂಲಕ ಪ್ರಯಾಣಿಸಲು ಇಂಧನದ ಘಟಕಗಳು ಬೇಕು.", + fuelExplanation: "ಅಕ್ಷರಗಳ ಧ್ವನಿಯನ್ನು ಚೆನ್ನಾಗಿ ಕೇಳುತ್ತ ನೀವು ಇಂಧನವನ್ನು ತಯಾರಿಸಬಹುದು!", + gameExplanation: "ನೀವು ಮೊದಲು ಒಂದು ಧ್ವನಿ ಕೇಳುವಿರಿ, ಆಮೇಲೆ ಒಂದು ಅಕ್ಷರ ನೋಡುವಿರಿ. ಅವೆರಡೂ ಹೊಂದಿಕೆ ಆಗುತ್ತಾ ಅಥವಾ ಇಲ್ಲವಾ ಅಂತ ಬೇಗನೆ ಹೇಳಿದ್ರೆ ನಿಮಗೆ ಹೆಚ್ಚು ಇಂಧನ ಸಿಗುತ್ತೆ!", + rocket: "🚀" + }, + practice: { + intro: "ನಿಜವಾದ ಪ್ರಯಾಣ ಶುರು ಮಾಡಕ್ಕೆ ಮೊದಲು ಸ್ವಲ್ಪ ಅಭ್ಯಾಸ ಮಾಡೋಣ!", + question: "ಅವು ಒಂದೇ ಧ್ವನಿಯನ್ನು ನೀಡುತ್ತವೆಯೇ?", + controls: "ನಿಮಗೆ ಬೇಕೆನ್ನಿಸಿದಂತೆ ಆಯ್ಕೆ ಮಾಡಬಹುದು!\n\nಧ್ವನಿ ಮತ್ತು ಅಕ್ಷರ ಹೊಂದಿಕೆಯಾಗುತ್ತೆ ಅಂತ ಅನ್ನಿಸಿದರೆ\n← ಕೀಯನ್ನು ಒತ್ತಿ.\n\nಅವು ಹೊಂದಿಕೆ ಆಗುವುದಿಲ್ಲ ಅಂತ ಅನ್ನಿಸಿದರೆ\n→ ಕೀಯನ್ನು ಒತ್ತಿ.\n\nಅಥವಾ ನೀವು ✓ ಅಥವಾ ✗ ಬಟನ್‌ಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಬಹುದು!\n\nನಿಮಗೆ ಸುಲಭವೆಂದು ಅನ್ನಿಸಿದ್ದು ಮಾಡಿ. ನೀವೇ ಈಗ ರಾಕೆಟ್‌ಗೆ ಕ್ಯಾಪ್ಟನ್!", + correctFeedback: "ಯೇ! ಅದು ನಮಗೆ ಸಹಾಯ ಮಾಡಿತು!", + wrongFeedback: "ಪರವಾಗಿಲ್ಲ! ಮತ್ತೆ ಪ್ರಯತ್ನಿಸೋಣ!", + greatJob: "ಉತ್ತಮ ಕೆಲಸ, ಕ್ಯಾಪ್ಟನ್ ರಾಹಿ! ನೀವು ಸಿದ್ಧರಾಗಿದ್ದೀರಿ!", + amazing: "ನೀವು ಅದ್ಭುತವಾಗಿ ಮಾಡುತ್ತಿದ್ದೀರಿ!", + allReady: "ಎಲ್ಲವೂ ಸಿದ್ಧವಾಗಿದೆ!" + }, + fuelExplanation: { + rilo: "ನೀವು ಪ್ರತಿ ಬಾರಿ ಧ್ವನಿ ಕೇಳಿ ಆಯ್ಕೆ ಮಾಡಿದಾಗ, ನಿಮ್ಮ ರಾಕೆಟ್‌ಗೆ ಹೆಚ್ಚು ಇಂಧನ ಸಿಗುತ್ತದೆ! ನೀವು ಪ್ರಶ್ನೆಗೆ ಬಹಳ ಬೇಗನೆ ಉತ್ತರಿಸಿದರೆ, ನಿಮಗೆ ಹೆಚ್ಚು ಇಂಧನ ಸಿಗುತ್ತದೆ!", + fuelMeter: "ಈಗ ಟ್ಯಾಂಕ್ ತುಂಬಿಸೋಣ, ಮತ್ತು ಗೆಲಾಕ್ಸಿಯನ್ನು ಅನ್ವೇಷಿಸೋಣ!!" + }, + readyToStart: { + rilo: "ನೀವು ಈಗ ಬಾಹ್ಯಾಕಾಶ ಪ್ರಯಾಣವನ್ನು ಪ್ರಾರಂಭಿಸಲು ಸಿದ್ಧರಾಗಿದ್ದೀರಾ, ಕ್ಯಾಪ್ಟನ್?", + button: "🚀 ಬಾಹ್ಯಾಕಾಶ ಪ್ರಯಾಣ ಪ್ರಾರಂಭಿಸಿ" + } + }, + mr: { + intro: { + narrator: "तुम्ही कॅप्टन राही आहात, एक धैर्यशील लहान अंतराळ यात्री.", + rocket: "तुमचा रॉकेट लॉन्च पॅडवर वाट पाहत आहे, चमकत आहे आणि शांतपणे गुणगुणत आहे.", + destination: "आज, तुम्ही अंतराळ प्रवास करत आहात!", + planet: "🌙" + }, + riloAppears: { + rilo: "कॅप्टन राही! आपला रॉकेट उडण्यासाठी तयार आहे पण त्याला इंधन हवे आहे!", + fuelRequirement: "आपल्याला गॅलॅक्सीमधून प्रवास करण्यासाठी इंधन युनिट्स हवे आहेत.", + fuelExplanation: "तुमच्या सुपर ऐकणाऱ्या कानांचा वापर करून आपण इंधन बनवू शकतो!", + gameExplanation: "तुम्ही एक आवाज ऐकाल, तुम्ही एक अक्षर पाहाल, आणि जर ते जुळत असतील, आपल्याला रॉकेटसाठी इंधन मिळेल!", + rocket: "🚀" + }, + practice: { + intro: "आपल्या खऱ्या प्रवासापूर्वी थोडे सराव करूया!", + question: "ते समान आवाज देतात का?", + controls: "तुम्ही तुमच्या स्वतःच्या मार्गाने निवडू शकता!\n\nजर तुम्हाला वाटत असेल की ते जुळतात,\n← की दाबा.\n\nजर तुम्हाला वाटत असेल की ते जुळत नाहीत,\n→ की दाबा.\n\nकिंवा तुम्ही फक्त ✓ किंवा ✗ बटणे क्लिक करू शकता!\n\nजे सर्वात सोपे वाटते,\nतुम्ही रॉकेटचे कॅप्टन आहात!", + correctFeedback: "होय! त्याने आम्हाला मदत केली!", + wrongFeedback: "ठीक आहे! पुन्हा प्रयत्न करूया!", + greatJob: "छान काम, कॅप्टन राही! तुम्ही तयार आहात!", + amazing: "तुम्ही खूप छान करत आहात!", + allReady: "सर्व तयार!" + }, + fuelExplanation: { + rilo: "जेव्हा तुम्ही ऐकता आणि निवडता, तेव्हा तुमच्या रॉकेटला अधिक इंधन मिळते! तुम्ही प्रश्नाला जितक्या वेगाने उत्तर द्याल, तितके अधिक इंधन मिळेल!", + fuelMeter: "टँक भरूया, आणि गॅलॅक्सीचा शोध घेऊया!!" + }, + readyToStart: { + rilo: "तुम्ही तुमचा अंतराळ प्रवास सुरू करण्यासाठी तयार आहात, कॅप्टन?", + button: "🚀 अंतराळ प्रवास सुरू करा" + } + }, + hi: { + intro: { + narrator: "तुम कैप्टन राही हो, एक बहादुर छोटे अंतरिक्ष यात्री।", + rocket: "तुम्हारा रॉकेट लॉन्च पैड पर इंतजार कर रहा है, चमक रहा है और धीरे-धीरे गुनगुना रहा है।", + destination: "आज, तुम अंतरिक्ष की यात्रा पर जा रहे हो!", + planet: "🌙" + }, + riloAppears: { + rilo: "कैप्टन राही! हमारा रॉकेट उड़ने के लिए तैयार है लेकिन इसे ईंधन चाहिए!", + fuelRequirement: "हमें आकाशगंगा में यात्रा करने के लिए ईंधन यूनिट्स चाहिए।", + fuelExplanation: "हम तुम्हारे सुपर सुनने वाले कानों का उपयोग करके ईंधन बना सकते हैं!", + gameExplanation: "तुम एक आवाज़ सुनोगे, तुम एक अक्षर देखोगे, और अगर वे मेल खाते हैं, तो हमें रॉकेट के लिए ईंधन मिलेगा!", + rocket: "🚀" + }, + practice: { + intro: "चलो असली यात्रा से पहले थोड़ा अभ्यास करते हैं!", + question: "क्या ये एक जैसे लगते हैं?", + controls: "तुम अपने तरीके से चुन सकते हो!\n\nअगर तुम्हें लगता है कि वे मेल खाते हैं,\n← की दबाओ।\n\nअगर तुम्हें लगता है कि वे मेल नहीं खाते,\n→ की दबाओ।\n\nया तुम बस ✓ या ✗ बटन पर क्लिक कर सकते हो!\n\nजो भी सबसे आसान लगे,\nतुम रॉकेट के कैप्टन हो!", + correctFeedback: "वाह! इसने हमारी मदद की!", + wrongFeedback: "कोई बात नहीं! फिर से कोशिश करते हैं!", + greatJob: "बहुत बढ़िया, कैप्टन राही! तुम तैयार हो!", + amazing: "तुम बहुत अच्छा कर रहे हो!", + allReady: "सब तैयार!" + }, + fuelExplanation: { + rilo: "हर बार जब तुम सुनते हो और चुनते हो, तुम्हारे रॉकेट को और ईंधन मिलता है! जितनी जल्दी तुम जवाब दोगे, उतना ज्यादा ईंधन मिलेगा!", + fuelMeter: "चलो टैंक भरते हैं, और आकाशगंगा की खोज करते हैं!!" + }, + readyToStart: { + rilo: "क्या तुम अपनी अंतरिक्ष यात्रा शुरू करने के लिए तैयार हो, कैप्टन?", + button: "🚀 अंतरिक्ष यात्रा शुरू करो" + } + } +}; + +export function LetterLauncherGameStoryPreview({ + onStartGame, + onBack, + level = 1, + hideHeader = false +}: LetterLauncherGameStoryPreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [storyPhase, setStoryPhase] = useState('intro'); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [showRilo, setShowRilo] = useState(false); + const [showRahi, setShowRahi] = useState(true); + const [practiceRound, setPracticeRound] = useState(0); + const [practiceQuestion, setPracticeQuestion] = useState(null); + const [showPracticeLetter, setShowPracticeLetter] = useState(false); + const [isPlayingPracticeAudio, setIsPlayingPracticeAudio] = useState(false); + const [practiceAnswer, setPracticeAnswer] = useState(null); + const [showPracticeFeedback, setShowPracticeFeedback] = useState(false); + const [isPracticeCorrect, setIsPracticeCorrect] = useState(false); + const [showControlsInstruction, setShowControlsInstruction] = useState(false); + const [controlsInstructionComplete, setControlsInstructionComplete] = useState(false); + const [highlightLeftButton, setHighlightLeftButton] = useState(false); + const [highlightRightButton, setHighlightRightButton] = useState(false); + const [currentSpeechIndex, setCurrentSpeechIndex] = useState(0); // Track which speech bubble to show + const [visibleSpeechBubbles, setVisibleSpeechBubbles] = useState([]); // Track visible bubbles + const [showPracticeCompletionMessage, setShowPracticeCompletionMessage] = useState(false); + const [practiceCompletionText, setPracticeCompletionText] = useState(''); + const [successfulPracticeAttempts, setSuccessfulPracticeAttempts] = useState(0); // Track successful attempts + const [showPracticeIntro, setShowPracticeIntro] = useState(false); // Track practice intro message + const [showFuelFlashMessages, setShowFuelFlashMessages] = useState(false); // Track fuel flash messages + const [visibleFlashNumber, setVisibleFlashNumber] = useState(null); // Track which flash number to show: 5, 3, or 1 + const [needsUserInteraction, setNeedsUserInteraction] = useState(false); // Track if autoplay is blocked + const [isWaitingForInteraction, setIsWaitingForInteraction] = useState(false); // Track if we're waiting for user interaction + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const story = storyScript[contentLanguage] || storyScript.en; + const requiredFuel = getFuelRequirement(level); + const missionDestination = getMissionDestination(level); + + // Generate practice questions + const generatePracticeQuestion = (): LetterLauncherQuestion => { + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (contentLanguage === 'en' || contentLanguage === 'te' || contentLanguage === 'mr' || contentLanguage === 'kn') + ? contentLanguage + : 'en'; + + const lettersToUse = memoryGameDataLoader.getLetters(supportedLanguage, 'basic'); + if (lettersToUse.length === 0) { + // Fallback + return { + audioLetter: 'A', + displayedLetter: 'A', + isMatch: true, + complexity: 'basic', + language: contentLanguage + }; + } + + const audioLetter = lettersToUse[Math.floor(Math.random() * lettersToUse.length)]; + const isMatch = Math.random() < 0.5; + const displayedLetter = isMatch + ? audioLetter + : lettersToUse.filter(l => l !== audioLetter)[Math.floor(Math.random() * (lettersToUse.length - 1))] || audioLetter; + + return { + audioLetter, + displayedLetter, + isMatch, + complexity: 'basic', + language: contentLanguage + }; + }; + + // Audio file mapping for story narration + const storyAudioMap: Record = { + // Intro phase + [story.intro.narrator]: 'intro_narrator.wav', + [story.intro.rocket]: 'intro_rocket.wav', + [story.intro.destination]: 'intro_destination.wav', + // Rilo appears phase + [story.riloAppears.rilo]: 'rilo_appears.wav', + [story.riloAppears.fuelRequirement]: 'fuel_requirement.wav', + [story.riloAppears.fuelExplanation]: 'fuel_explanation.wav', + [story.riloAppears.gameExplanation]: 'game_explanation.wav', + // Practice phase + [story.practice.intro]: 'practice_intro.wav', + [story.practice.question]: 'practice_question.wav', + [story.practice.controls]: 'practice_controls.wav', + [story.practice.correctFeedback]: 'correct_feedback.wav', + [story.practice.wrongFeedback]: 'wrong_feedback.wav', + [story.practice.greatJob]: 'great_job.wav', + [story.practice.amazing]: 'amazing.wav', + [story.practice.allReady]: 'all_ready.wav', + // Fuel explanation phase + [story.fuelExplanation.rilo]: 'fuel_rilo.wav', + [story.fuelExplanation.fuelMeter]: 'fuel_meter.wav', + // Ready to start phase + [story.readyToStart.rilo]: 'ready_to_start.wav', + }; + + // Reference to current audio element for cleanup + const currentAudioRef = useRef(null); + // Reference to track if we're in a replay operation to prevent double audio + const isReplayingRef = useRef(false); + // Reference to track if narration is actually playing (more reliable than state) + const isPlayingNarrationRef = useRef(false); + + // Play audio file from public folder + const playAudioFile = async (filename: string): Promise => { + return new Promise((resolve) => { + const audioPath = `/audio/audio-preview/combined-letter-games/letter-launcher-story/${audioLanguage}/${filename}`; + const audio = new Audio(audioPath); + // attach the slow audio toast + attachSlowLoadToast(audio); + currentAudioRef.current = audio; + + let resolved = false; + let audioStarted = false; + + const cleanup = () => { + if (currentAudioRef.current === audio) { + currentAudioRef.current = null; + } + audio.onloadeddata = null; + audio.oncanplay = null; + audio.onplay = null; + audio.onended = null; + audio.onerror = null; + }; + + // Timeout to prevent hanging if audio never loads (15 seconds) + // BUT: if audio has started playing, don't timeout - wait for it to finish + const timeout = setTimeout(() => { + if (!resolved) { + // Check if audio is actually playing (check both flag and actual audio state) + // This handles race conditions where audio might be playing but flag not set yet + const isActuallyPlaying = !audio.paused && !audio.ended && audio.currentTime > 0; + if (audioStarted || isActuallyPlaying) { + // Audio is playing or about to play, so wait for it to finish naturally + // Don't resolve yet, let the onended handler handle it + // But set up a longer safety timeout in case audio never ends + setTimeout(() => { + if (!resolved) { + resolved = true; + console.warn(`Audio playback extended timeout: ${audioPath}`); + cleanup(); + resolve(true); // Resolve as true since audio was playing + } + }, 30000); // Additional 30 seconds for audio to finish + return; + } + // Audio hasn't started, so it's a real timeout + resolved = true; + console.warn(`Audio load timeout: ${audioPath}`); + audio.pause(); + audio.currentTime = 0; + cleanup(); + resolve(false); + } + }, 15000); + + const tryPlay = () => { + if (resolved) return; + audio.play().then(() => { + audioStarted = true; + // Set up ended handler + audio.onended = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + cleanup(); + resolve(true); + } + }; + // Set up error handler for playback errors + audio.onerror = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.warn(`Audio play failed: ${audioPath}`); + cleanup(); + resolve(false); + } + }; + }).catch((error) => { + if (!resolved) { + // Check if error is due to autoplay policy (user interaction required) + const isAutoplayError = error.name === 'NotAllowedError' || + (error as Error).message?.includes('user didn\'t interact') || + (error as Error).message?.includes('play() failed'); + + if (isAutoplayError) { + // Set flag to indicate we need user interaction + setNeedsUserInteraction(true); + setIsWaitingForInteraction(true); + + // Store audio reference for retry after interaction + // Don't cleanup yet - we'll retry after user interaction + // Resolve as false so playNarration can handle the retry + resolved = true; + clearTimeout(timeout); + // Don't cleanup - keep audio reference for retry + resolve(false); + return; + } + + // Other errors - resolve as false + resolved = true; + clearTimeout(timeout); + console.warn(`Audio play failed: ${audioPath}`, error); + cleanup(); + resolve(false); + } + }); + }; + + // Wait for audio data to load before playing + audio.onloadeddata = () => { + if (resolved) return; + tryPlay(); + }; + + // Also listen for canplay event as fallback (some browsers fire this instead) + audio.oncanplay = () => { + if (!audioStarted && !resolved) { + tryPlay(); + } + }; + + // Track when audio actually starts playing + audio.onplay = () => { + audioStarted = true; + // Clear the timeout since audio has started - it will finish naturally + clearTimeout(timeout); + }; + + // Handle loading errors + audio.onerror = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.warn(`Audio file not found or failed to load: ${audioPath}`); + cleanup(); + resolve(false); + } + }; + + // Explicitly start loading the audio + audio.load(); + }); + }; + + // Wait for speech synthesis voices to load (important for refresh) + const waitForVoices = (): Promise => { + return new Promise((resolve) => { + if (!('speechSynthesis' in window)) { + resolve(); + return; + } + + // Check if voices are already loaded + const voices = speechSynthesis.getVoices(); + if (voices.length > 0) { + resolve(); + return; + } + + // Wait for voiceschanged event + const onVoicesChanged = () => { + const voices = speechSynthesis.getVoices(); + if (voices.length > 0) { + speechSynthesis.onvoiceschanged = null; + resolve(); + } + }; + + // Remove any existing listener first + if (speechSynthesis.onvoiceschanged) { + speechSynthesis.onvoiceschanged = null; + } + + speechSynthesis.onvoiceschanged = onVoicesChanged; + + // Trigger voices check (some browsers need this) + speechSynthesis.getVoices(); + + // Fallback timeout in case voiceschanged never fires + setTimeout(() => { + if (speechSynthesis.onvoiceschanged === onVoicesChanged) { + speechSynthesis.onvoiceschanged = null; + } + resolve(); // Resolve even if no voices found to prevent hanging + }, 2000); + }); + }; + + // Fallback to Web Speech API + const playWebSpeechFallback = async (text: string): Promise => { + if (!('speechSynthesis' in window)) { + console.warn('Speech synthesis not supported'); + return; + } + + // Wait for voices to load (important for refresh) + await waitForVoices(); + + return new Promise((resolve) => { + const utterance = new SpeechSynthesisUtterance(text); + + // Set language + utterance.lang = audioLanguage === 'te' ? 'te-IN' : + audioLanguage === 'kn' ? 'kn-IN' : + audioLanguage === 'mr' ? 'mr-IN' : + audioLanguage === 'hi' ? 'hi-IN' : 'en-US'; + + // Child-friendly speech rates (slower for clarity) + switch (audioLanguage) { + case 'te': + case 'kn': + case 'mr': + case 'hi': + utterance.rate = 0.75; + utterance.pitch = 0.95; + utterance.volume = 1.0; + break; + default: + utterance.rate = 0.8; + utterance.pitch = 1.0; + utterance.volume = 1.0; + } + + // Try to find the best voice + const voices = speechSynthesis.getVoices(); + let selectedVoice = voices.find(voice => voice.lang === utterance.lang); + + if (!selectedVoice) { + const langCode = utterance.lang.split('-')[0]; + selectedVoice = voices.find(voice => voice.lang.startsWith(langCode)); + } + + if (selectedVoice) { + utterance.voice = selectedVoice; + } + + utterance.onend = () => resolve(); + utterance.onerror = () => resolve(); + + speechSynthesis.speak(utterance); + }); + }; + + // Play narration - tries audio file first, falls back to Web Speech API + const playNarration = async (text: string) => { + // Cancel any existing audio/speech before starting new one + // This ensures we can start new narration even if previous one is finishing + if (currentAudioRef.current) { + currentAudioRef.current.pause(); + currentAudioRef.current.currentTime = 0; + currentAudioRef.current = null; + } + if ('speechSynthesis' in window && speechSynthesis.speaking) { + speechSynthesis.cancel(); + await new Promise(resolve => setTimeout(resolve, 200)); + } + + // Reset the playing state (both ref and state) after canceling existing audio + // This ensures we can start new narration even if previous one was finishing + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + + // Small delay to ensure cleanup is complete and state updates propagate + await new Promise(resolve => setTimeout(resolve, 100)); + + // Set both ref and state to track playing status + // We don't need to check here because we already canceled everything above + isPlayingNarrationRef.current = true; + setIsPlayingNarration(true); + try { + // Get the audio filename for this text + const audioFilename = storyAudioMap[text]; + + if (audioFilename) { + // Try to play the audio file + const success = await playAudioFile(audioFilename); + if (success) { + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + return; + } + + // If autoplay was blocked, wait for user interaction and retry + if (needsUserInteraction || isWaitingForInteraction) { + // Wait for user interaction + while (needsUserInteraction || isWaitingForInteraction) { + await new Promise(resolve => setTimeout(resolve, 100)); + } + + // Try playing the existing audio element first (if it exists and is valid) + if (currentAudioRef.current && !currentAudioRef.current.ended) { + try { + await currentAudioRef.current.play(); + // Audio started playing, wait for it to finish + await new Promise((resolve) => { + const audio = currentAudioRef.current; + if (!audio) { + resolve(); + return; + } + + const onEnded = () => { + audio.removeEventListener('ended', onEnded); + resolve(); + }; + + const onError = () => { + audio.removeEventListener('error', onError); + resolve(); + }; + + audio.addEventListener('ended', onEnded); + audio.addEventListener('error', onError); + + // Safety timeout + setTimeout(() => { + audio.removeEventListener('ended', onEnded); + audio.removeEventListener('error', onError); + resolve(); + }, 30000); + }); + + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + return; + } catch (error) { + // Failed to play existing audio, create new one + console.warn('Retry with existing audio failed, creating new audio element'); + } + } + + // Retry playing the audio file after interaction (create new audio element) + const retrySuccess = await playAudioFile(audioFilename); + if (retrySuccess) { + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + return; + } + } + + // Check if audio is actually playing even though playAudioFile returned false + // This can happen if audio started playing but timed out during load + if (currentAudioRef.current && !currentAudioRef.current.paused && !currentAudioRef.current.ended) { + // Audio is playing, wait for it to finish + await new Promise((resolve) => { + const audio = currentAudioRef.current; + if (!audio) { + resolve(); + return; + } + + const onEnded = () => { + audio.removeEventListener('ended', onEnded); + resolve(); + }; + + const onError = () => { + audio.removeEventListener('error', onError); + resolve(); + }; + + audio.addEventListener('ended', onEnded); + audio.addEventListener('error', onError); + + // Safety timeout in case audio never ends + setTimeout(() => { + audio.removeEventListener('ended', onEnded); + audio.removeEventListener('error', onError); + resolve(); + }, 30000); // 30 second max wait + }); + + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + return; + } + } + + // Fallback to Web Speech API if audio file not found or failed + console.log('Using Web Speech API fallback for:', text.substring(0, 50) + '...'); + await playWebSpeechFallback(text); + } catch (error) { + console.warn('Narration playback failed:', error); + } finally { + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + } + }; + + // Handle story phase progression + useEffect(() => { + let mounted = true; + + const handlePhase = async () => { + // Skip if we're in the middle of a replay operation (preventing double audio) + if (isReplayingRef.current && storyPhase === 'readyToStart') { + return; + } + + // Wait for user interaction if autoplay is blocked (important for refresh) + if (needsUserInteraction || isWaitingForInteraction) { + // Wait for user interaction before proceeding + while (needsUserInteraction || isWaitingForInteraction) { + await new Promise(resolve => setTimeout(resolve, 100)); + if (!mounted) return; + } + } + + // Wait for any ongoing narration to complete before proceeding (important for refresh) + if (isPlayingNarration) { + // Wait a bit and check again + await new Promise(resolve => setTimeout(resolve, 100)); + if (isPlayingNarration) { + // Still playing, skip this cycle + return; + } + } + + // On initial mount (especially after refresh), add a small delay to ensure audio is ready + if (storyPhase === 'intro' && !isReplayingRef.current) { + await new Promise(resolve => setTimeout(resolve, 300)); + if (!mounted) return; + } + + switch (storyPhase) { + case 'intro': + if (!mounted) return; + // Clear replay flag when starting intro + isReplayingRef.current = false; + // Show narrator bubble and start audio + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([0]); + await playNarration(story.intro.narrator); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Show rocket bubble and start audio (clear previous) + if (!mounted) return; + setCurrentSpeechIndex(1); + setVisibleSpeechBubbles([1]); // Only show current bubble + await playNarration(story.intro.rocket); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Show Captain Rahi and destination bubble, start audio (clear previous) + if (!mounted) return; + setShowRahi(true); + setCurrentSpeechIndex(2); + setVisibleSpeechBubbles([2]); // Only show current bubble + await playNarration(story.intro.destination); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 1000)); + + if (mounted) { + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([]); + setStoryPhase('riloAppears'); + } + break; + case 'riloAppears': + if (!mounted) return; + // Show both Rahi and Rilo for the conversation + setShowRahi(true); + setShowRilo(true); + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([]); + await new Promise(resolve => setTimeout(resolve, 300)); + + // First Rilo speech - show bubble and start audio + if (!mounted) return; + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([0]); // Only show current bubble + await playNarration(story.riloAppears.rilo); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Second Rilo speech - show bubble and start audio (clear previous) + if (!mounted) return; + setCurrentSpeechIndex(1); + setVisibleSpeechBubbles([1]); // Only show current bubble + await playNarration(story.riloAppears.fuelRequirement); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Third Rilo speech - show bubble and start audio (clear previous) + if (!mounted) return; + setCurrentSpeechIndex(2); + setVisibleSpeechBubbles([2]); // Only show current bubble + await playNarration(story.riloAppears.fuelExplanation); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Fourth Rilo speech - show bubble and start audio (clear previous) + if (!mounted) return; + setCurrentSpeechIndex(3); + setVisibleSpeechBubbles([3]); // Only show current bubble + await playNarration(story.riloAppears.gameExplanation); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Fifth Rilo speech - "Let's practice a tiny bit..." - show bubble and start audio (clear previous) + if (!mounted) return; + setCurrentSpeechIndex(4); + setVisibleSpeechBubbles([4]); // Only show current bubble + await playNarration(story.practice.intro); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 1000)); + + if (mounted) { + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([]); + setStoryPhase('practice1'); + } + break; + case 'practice1': + case 'practice2': + case 'practice3': + if (!mounted) return; + const roundNum = storyPhase === 'practice1' ? 1 : storyPhase === 'practice2' ? 2 : 3; + setPracticeRound(roundNum); + const question = generatePracticeQuestion(); + setPracticeQuestion(question); + setShowPracticeLetter(false); + setPracticeAnswer(null); + setShowPracticeFeedback(false); + setShowControlsInstruction(false); + setControlsInstructionComplete(false); + setHighlightLeftButton(false); + setHighlightRightButton(false); + setShowPracticeCompletionMessage(false); + setPracticeCompletionText(''); + setShowPracticeIntro(false); + + // Reset successful attempts counter only when starting practice1 + if (roundNum === 1) { + setSuccessfulPracticeAttempts(0); + } + + // Ensure Rilo is visible for practice rounds + setShowRilo(true); + setShowRahi(false); + + // For rounds 2 and 3, enable question window immediately (no controls instruction) + if (roundNum > 1) { + setControlsInstructionComplete(true); + } + + if (roundNum === 1) { + // Skip practice intro (already shown in riloAppears phase) and go straight to controls instruction + setShowPracticeIntro(false); + setShowControlsInstruction(true); + setControlsInstructionComplete(false); + + // Play audio letter while controls are visible + setIsPlayingPracticeAudio(true); + await playLetterAudio(question.audioLetter, contentLanguage); + if (!mounted) return; + setIsPlayingPracticeAudio(false); + setShowPracticeLetter(true); + + // Now speak the controls instruction + await playNarration(story.practice.controls); + if (!mounted) return; + // After narration completes, enable the question window + setControlsInstructionComplete(true); + // After narration, show hand pointer and highlight buttons sequentially + await new Promise(resolve => setTimeout(resolve, 500)); + setHighlightLeftButton(true); + await new Promise(resolve => setTimeout(resolve, 1500)); + setHighlightLeftButton(false); + await new Promise(resolve => setTimeout(resolve, 300)); + setHighlightRightButton(true); + await new Promise(resolve => setTimeout(resolve, 1500)); + setHighlightRightButton(false); + // Keep instruction visible but remove highlights + await new Promise(resolve => setTimeout(resolve, 500)); + } else { + // Rounds 2 and 3 - just play audio and show letter + setIsPlayingPracticeAudio(true); + await playLetterAudio(question.audioLetter, contentLanguage); + if (!mounted) return; + setIsPlayingPracticeAudio(false); + setShowPracticeLetter(true); + } + break; + case 'fuelExplanation': + if (!mounted) return; + // Ensure Rilo is visible + setShowRilo(true); + setShowRahi(false); + + // First Rilo speech - show bubble and start audio + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([0]); // Only show current bubble + setShowFuelFlashMessages(true); // Show flash messages + setVisibleFlashNumber(null); // Reset flash number + await playNarration(story.fuelExplanation.rilo); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 300)); + + // Show flash messages sequentially: +5, then +3, then +1 + if (mounted) { + // Show +5 + setVisibleFlashNumber(5); + await new Promise(resolve => setTimeout(resolve, 1000)); + if (!mounted) return; + + // Show +3 + setVisibleFlashNumber(3); + await new Promise(resolve => setTimeout(resolve, 1000)); + if (!mounted) return; + + // Show +1 + setVisibleFlashNumber(1); + await new Promise(resolve => setTimeout(resolve, 1000)); + if (!mounted) return; + } + + // Second Rilo speech - show bubble and start audio (clear previous) + if (!mounted) return; + setCurrentSpeechIndex(1); + setVisibleSpeechBubbles([1]); // Only show current bubble + setShowFuelFlashMessages(false); // Hide flash messages + setVisibleFlashNumber(null); // Reset flash number + await playNarration(story.fuelExplanation.fuelMeter); + if (!mounted) return; + await new Promise(resolve => setTimeout(resolve, 1000)); + + if (mounted) { + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([]); + setStoryPhase('readyToStart'); + } + break; + case 'readyToStart': + if (!mounted) return; + // Ensure Rilo is visible + setShowRilo(true); + setShowRahi(false); + + // Show Rilo speech bubble and start audio + setCurrentSpeechIndex(0); + setVisibleSpeechBubbles([0]); + await playNarration(story.readyToStart.rilo); + break; + } + }; + + handlePhase(); + + return () => { + mounted = false; + // Stop any ongoing audio when phase changes + if (currentAudioRef.current) { + currentAudioRef.current.pause(); + currentAudioRef.current.currentTime = 0; + currentAudioRef.current = null; + } + // Cancel any speech synthesis + if ('speechSynthesis' in window && speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + }; + }, [storyPhase, story, contentLanguage, audioLanguage, needsUserInteraction, isWaitingForInteraction]); + + // Handle practice answer + const handlePracticeAnswer = useCallback(async (isMatch: boolean) => { + if (!practiceQuestion || showPracticeFeedback || isPlayingPracticeAudio) return; + + setPracticeAnswer(isMatch); + const isCorrect = isMatch === practiceQuestion.isMatch; + setIsPracticeCorrect(isCorrect); + setShowPracticeFeedback(true); + + // Show feedback message + let newSuccessfulCount = successfulPracticeAttempts; + if (isCorrect) { + // Play success sound like Letter Hunt game + await playSuccessSound(audioLanguage, { exactLanguage: true }); + // Increment successful attempts counter + newSuccessfulCount = successfulPracticeAttempts + 1; + setSuccessfulPracticeAttempts(newSuccessfulCount); + } else { + await playNarration(story.practice.wrongFeedback); + } + + await new Promise(resolve => setTimeout(resolve, 1500)); + setShowPracticeFeedback(false); + + // Move to next phase - use current storyPhase from state + const currentPhase = storyPhase; + if (currentPhase === 'practice1') { + // Only move to practice2 if we have at least 1 successful attempt + if (newSuccessfulCount >= 1) { + setStoryPhase('practice2'); + } else { + // Stay in practice1 - generate new question for retry + // Don't show controls instruction again, enable immediately + setControlsInstructionComplete(true); + const newQuestion = generatePracticeQuestion(); + setPracticeQuestion(newQuestion); + setShowPracticeLetter(false); + setPracticeAnswer(null); + setShowPracticeFeedback(false); + // Play audio and show letter again + setIsPlayingPracticeAudio(true); + await playLetterAudio(newQuestion.audioLetter, contentLanguage); + setIsPlayingPracticeAudio(false); + setShowPracticeLetter(true); + } + } else if (currentPhase === 'practice2') { + // Only move to practice3 if we have at least 2 successful attempts + if (newSuccessfulCount >= 2) { + setStoryPhase('practice3'); + } else { + // Stay in practice2 - generate new question for retry + const newQuestion = generatePracticeQuestion(); + setPracticeQuestion(newQuestion); + setShowPracticeLetter(false); + setPracticeAnswer(null); + setShowPracticeFeedback(false); + // Play audio and show letter again + setIsPlayingPracticeAudio(true); + await playLetterAudio(newQuestion.audioLetter, contentLanguage); + setIsPlayingPracticeAudio(false); + setShowPracticeLetter(true); + } + } else if (currentPhase === 'practice3') { + // After practice3, only move forward if we have 3 successful attempts + if (newSuccessfulCount >= 3) { + // Hide the practice question window first + setPracticeQuestion(null); + setShowPracticeLetter(false); + + // Show completion conversation after 3 successful attempts + setPracticeCompletionText(story.practice.allReady); + setShowPracticeCompletionMessage(true); + await playNarration(story.practice.allReady); + await new Promise(resolve => setTimeout(resolve, 1500)); + setShowPracticeCompletionMessage(false); + setStoryPhase('fuelExplanation'); + } else { + // Stay in practice3 - generate new question for retry + const newQuestion = generatePracticeQuestion(); + setPracticeQuestion(newQuestion); + setShowPracticeLetter(false); + setPracticeAnswer(null); + setShowPracticeFeedback(false); + // Play audio and show letter again + setIsPlayingPracticeAudio(true); + await playLetterAudio(newQuestion.audioLetter, contentLanguage); + setIsPlayingPracticeAudio(false); + setShowPracticeLetter(true); + } + } + }, [practiceQuestion, showPracticeFeedback, isPlayingPracticeAudio, storyPhase, story, audioLanguage, successfulPracticeAttempts]); + + // Handle keyboard input for practice rounds (Left/W = Match, Right/M = Non-match) + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + if (showPracticeFeedback || !practiceQuestion || !showPracticeLetter || isPlayingPracticeAudio) return; + + // For practice round 1, only allow input after instruction conversation completes + if (practiceRound === 1 && !controlsInstructionComplete) return; + + if (event.key === 'ArrowLeft' || event.key === 'w' || event.key === 'W') { + event.preventDefault(); + handlePracticeAnswer(true); // Match + } else if (event.key === 'ArrowRight' || event.key === 'm' || event.key === 'M') { + event.preventDefault(); + handlePracticeAnswer(false); // Non-match + } + }; + + // Only add listener during practice rounds + if (storyPhase === 'practice1' || storyPhase === 'practice2' || storyPhase === 'practice3') { + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + } + }, [storyPhase, showPracticeFeedback, practiceQuestion, showPracticeLetter, isPlayingPracticeAudio, handlePracticeAnswer, practiceRound, controlsInstructionComplete]); + + // Cleanup audio on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Replay story handler + const handleReplayStory = async () => { + // Stop all audio first + stopAllAudio(); + + // Clear audio ref + if (currentAudioRef.current) { + currentAudioRef.current.pause(); + currentAudioRef.current.currentTime = 0; + currentAudioRef.current = null; + } + + // Cancel any speech synthesis and wait for it to fully stop + if ('speechSynthesis' in window) { + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + // Wait a bit for speech synthesis to fully cancel + await new Promise(resolve => setTimeout(resolve, 150)); + } + } + + // Reset all state to beginning + isPlayingNarrationRef.current = false; + setIsPlayingNarration(false); + setNeedsUserInteraction(false); + setIsWaitingForInteraction(false); + setShowRilo(false); + setShowRahi(true); + setPracticeRound(0); + setPracticeQuestion(null); + setShowPracticeLetter(false); + setIsPlayingPracticeAudio(false); + setPracticeAnswer(null); + setShowPracticeFeedback(false); + setIsPracticeCorrect(false); + setShowControlsInstruction(false); + setControlsInstructionComplete(false); + setHighlightLeftButton(false); + setHighlightRightButton(false); + setShowPracticeCompletionMessage(false); + setPracticeCompletionText(''); + setSuccessfulPracticeAttempts(0); + setShowPracticeIntro(false); + setShowFuelFlashMessages(false); + setVisibleFlashNumber(null); + + // Reset story phase and speech bubble state + // Set replay flag to prevent double audio during phase transition + isReplayingRef.current = true; + setCurrentSpeechIndex(0); + + if (storyPhase === 'intro') { + // Temporarily set to different phase to force useEffect to re-run + setStoryPhase('readyToStart'); + setVisibleSpeechBubbles([]); + // Use a delay to ensure the phase change is processed and any cleanup runs + // This prevents double audio by ensuring the intermediate phase doesn't play audio + setTimeout(() => { + // Clear flag and set phase in separate updates to ensure smooth transition + isReplayingRef.current = false; + // Use requestAnimationFrame to ensure flag is cleared before phase change renders + requestAnimationFrame(() => { + setStoryPhase('intro'); + }); + }, 100); + } else { + // Set initial visibility immediately to prevent blank screen + setVisibleSpeechBubbles([0]); + isReplayingRef.current = false; // Clear flag immediately for non-intro case + setStoryPhase('intro'); + } + }; + + // Back handler with audio cleanup + const handleBack = () => { + stopAllAudio(); + + // Clear audio ref + if (currentAudioRef.current) { + currentAudioRef.current.pause(); + currentAudioRef.current = null; + } + + // Cancel any speech synthesis + if ('speechSynthesis' in window && speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + + onBack(); + }; + + // Start game handler + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Render speech bubble with smooth fade animation (no shrink/wobble) + const SpeechBubble = ({ text, character, position = 'left', index, isVisible = true }: { text: string; character: 'rahi' | 'rilo' | 'narrator'; position?: 'left' | 'right' | 'center'; index?: number; isVisible?: boolean }) => { + const characterEmoji = character === 'rahi' ? '👨‍🚀' : character === 'rilo' ? '🤖' : '✨'; + const characterName = character === 'rahi' ? 'Captain Rahi' : character === 'rilo' ? 'Rilo' : ''; + + if (!text) return null; + + return ( +
+
+ {/* Speech bubble */} +
+

+ {text} +

+
+ {/* Arrow pointing DOWN to character below */} +
+
+
+
+
+ ); + }; + + // Render character with smooth fade only (no scale/wobble) + const CharacterDisplay = ({ character, show }: { character: 'rahi' | 'rilo'; show: boolean }) => { + const emoji = character === 'rahi' ? '👨‍🚀' : '🤖'; + // Fixed sizes for consistency + const size = character === 'rahi' + ? 'w-16 h-16 sm:w-20 sm:h-20 md:w-24 md:h-24 text-5xl sm:text-6xl md:text-7xl' + : 'w-14 h-14 sm:w-18 sm:h-18 md:w-20 md:h-20 text-4xl sm:text-5xl md:text-6xl'; + + return ( +
+ {emoji} +
+ ); + }; + + return ( + +
+ {/* Header */} + {!hideHeader && ( +
+ +
+ + +
+
+ )} + + {/* User Interaction Required Overlay */} + {needsUserInteraction && ( +
{ + setNeedsUserInteraction(false); + setIsWaitingForInteraction(false); + }} + > + e.stopPropagation()} + > +
+

+ {contentLanguage === 'en' ? 'Click to Start' : + contentLanguage === 'te' ? 'ప్రారంభించడానికి క్లిక్ చేయండి' : + contentLanguage === 'kn' ? 'ಪ್ರಾರಂಭಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ' : + contentLanguage === 'mr' ? 'प्रारंभ करण्यासाठी क्लिक करा' : + 'Click to Start'} +

+

+ {contentLanguage === 'en' ? 'Please click anywhere to start the preview' : + contentLanguage === 'te' ? 'దయచేసి ప్రివ్యూ ప్రారంభించడానికి ఎక్కడైనా క్లిక్ చేయండి' : + contentLanguage === 'kn' ? 'ದಯವಿಟ್ಟು ಪೂರ್ವವೀಕ್ಷಣೆಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಯಾವುದೇ ಸ್ಥಳದಲ್ಲಿ ಕ್ಲಿಕ್ ಮಾಡಿ' : + contentLanguage === 'mr' ? 'कृपया पूर्वावलोकन सुरू करण्यासाठी कुठेही क्लिक करा' : + 'Please click anywhere to start the preview'} +

+ +
+
+
+ )} + + {/* Main Story Content */} + +
+ {/* Story Scene - Conversation Layout */} + {/* Hide content during replay transition to prevent flash */} + {!isReplayingRef.current && (storyPhase === 'intro' || storyPhase === 'riloAppears') && ( +
+ {/* Intro Phase - Character FIXED position with speech bubble above */} + {storyPhase === 'intro' && ( + <> + {/* Planet destination - positioned top right - lower z-index to be behind speech bubble */} + {visibleSpeechBubbles.length > 0 && ( +
+
+ +
+
+ )} + + {/* Character FIXED in center - speech bubble positioned absolutely above */} +
+ {/* Speech bubble - positioned absolutely above character, doesn't affect layout */} +
0 ? 1 : 0, + transition: 'opacity 0.5s ease-in-out', + pointerEvents: visibleSpeechBubbles.length > 0 ? 'auto' : 'none', + width: 'clamp(160px, 80vw, 320px)', + maxWidth: 'calc(100vw - 1rem)' + }} + > + 0} + /> +
+ + {/* Character FIXED in center of screen */} +
+ 0} /> +
+ + {/* Planets image with orbiting rocket - only show after "Today, you're going on a space trip" conversation, below Rahi */} + {/* Commented out - can be uncommented later if needed */} + {/* {visibleSpeechBubbles.includes(2) && ( +
+ {/* Planets image */} + {/* Planets */} + {/* Orbit path */} + {/*
*/} + {/* Orbiting rocket container */} + {/*
*/} + {/* Rocket positioned at edge, pointing tangent to orbit */} + {/*
*/} + {/* */} + {/* Rocket body */} + {/* */} + {/* */} + {/* Nose cone */} + {/* */} + {/* Window */} + {/* */} + {/* */} + {/* Fins */} + {/* */} + {/* */} + {/* Flame */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/*
*/} + {/*
*/} + {/* )} */} +
+ + )} + + {/* Rilo Appears Phase - Characters with speech bubble pointing to Rilo */} + {storyPhase === 'riloAppears' && ( +
+ {/* Moon and rocket animation - fixed position top right corner */} +
+
+ +
+
+ + {/* Characters on same row - Rahi left, Rilo right */} +
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+
+ 👨‍🚀 +
+
+
+ + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+ {/* Speech bubble - positioned absolutely above Rilo, pointing to Rilo */} + {/* Centered above Rilo, but constrained to stay within viewport on mobile */} +
0 ? 1 : 0, + transition: 'opacity 0.5s ease-in-out', + pointerEvents: visibleSpeechBubbles.length > 0 ? 'auto' : 'none', + width: 'clamp(120px, 70vw, 300px)', + maxWidth: 'min(calc(100vw - 3rem), 300px)', + left: '50%', + transform: 'translateX(-50%)', + // On mobile, ensure it doesn't overflow right edge + right: 'auto' + }} + > + 0} + /> +
+ + {/* Rilo character - same vertical position as Rahi */} +
+
+ 🤖 +
+
+
+
+
+ )} +
+ )} + + {/* Practice Rounds */} + {(storyPhase === 'practice1' || storyPhase === 'practice2' || storyPhase === 'practice3') && (practiceQuestion || showPracticeCompletionMessage || showPracticeIntro) && ( +
+ {/* Practice intro message - "Let's practice a tiny bit" */} + {showPracticeIntro && ( +
+ {/* Characters on same row - Rahi left, Rilo right */} +
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+
+ 👨‍🚀 +
+
+
+ + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+ {/* Speech bubble - positioned absolutely above Rilo, pointing to Rilo */} + {/* Centered above Rilo, but constrained to stay within viewport on mobile */} +
+ +
+ + {/* Rilo character - same vertical position as Rahi */} +
+
+ 🤖 +
+
+
+
+
+ )} + + {/* Completion message - centered full width when no practice question */} + {showPracticeCompletionMessage && practiceCompletionText && !practiceQuestion && !showPracticeIntro && ( +
+ {/* Characters on same row - Rahi left, Rilo right */} +
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+ 👨‍🚀 +
+
+ + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+ {/* Speech bubble - positioned absolutely above Rilo, pointing to Rilo */} + {/* Centered above Rilo, but constrained to stay within viewport on mobile */} +
+ +
+ + {/* Rilo character - same vertical position as Rahi */} +
+ 🤖 +
+
+
+
+ )} + + {/* Practice with conversation - Grid layout for round 1 */} + {practiceQuestion && practiceRound === 1 && showControlsInstruction && !showPracticeIntro && ( +
+
+ {/* Left side - Question Box */} +
+
+
+ + + {/* Hand pointer pointing to buttons after instruction completes */} + {controlsInstructionComplete && showPracticeLetter && !showPracticeFeedback && ( +
+
+
👆
+
+
+ )} +
+
+
+ + {/* Right side - Empty space for conversation bubble */} +
+ {/* Space reserved for conversation bubble positioned above Rilo */} +
+
+ + {/* Characters at bottom - Rahi left, Rilo right */} +
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+ 👨‍🚀 +
+
+ + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+ {/* Speech bubble - positioned absolutely above Rilo, pointing to Rilo */} + {/* On mobile: positioned higher to avoid overlap with game card */} +
+ +
+ + {/* Rilo character */} +
+ 🤖 +
+
+
+
+ )} + + {/* Practice without conversation - Centered game area for rounds 2 and 3 only */} + {practiceQuestion && !showPracticeIntro && practiceRound > 1 && ( +
+ {/* Centered Question Container */} +
+
+
+
+ +
+
+
+
+ + {/* Characters at bottom - Rahi left, Rilo right */} +
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+ 👨‍🚀 +
+
+ + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+
+ 🤖 +
+
+
+
+ )} +
+ )} + + {/* Fuel Explanation */} + {storyPhase === 'fuelExplanation' && ( +
+ {/* Moon with rocket animation - centered */} +
+
+ +
+
+ + {/* Flash messages for fuel amounts - centered between characters, below moon */} + {showFuelFlashMessages && visibleSpeechBubbles.includes(0) && ( +
+ {visibleFlashNumber === 5 && ( + +5 + )} + {visibleFlashNumber === 3 && ( + <> + +5 + +3 + + )} + {visibleFlashNumber === 1 && ( + <> + +5 + +3 + +1 + + )} +
+ )} + + {/* Characters on same row - Rahi left, Rilo right, Fuel card in center (desktop) or below (mobile) */} +
+
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+ 👨‍🚀 +
+
+ + {/* Fuel meter visualization - in center between characters on desktop only */} + {visibleSpeechBubbles.includes(1) && ( +
+ } + label="Fuel Needed" + rightIcon={} + alignment="end" + /> +
+ )} + + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+ {/* Speech bubble - positioned absolutely above Rilo, pointing to Rilo */} + {/* Centered above Rilo, but constrained to stay within viewport on mobile */} +
0 ? 1 : 0, + transition: 'opacity 0.5s ease-in-out', + pointerEvents: visibleSpeechBubbles.length > 0 ? 'auto' : 'none', + width: 'clamp(120px, 70vw, 320px)', + maxWidth: 'min(calc(100vw - 3rem), 320px)', + left: '50%', + transform: 'translateX(-50%)', + right: 'auto' + }} + > + {/* Speech bubbles - show progressively */} +
+ {visibleSpeechBubbles.includes(0) && ( + + )} + {visibleSpeechBubbles.includes(1) && ( + + )} +
+
+ + {/* Rilo character - same vertical position as Rahi */} +
+
+ 🤖 +
+
+
+
+ + {/* Mobile: Fuel card below characters */} + {visibleSpeechBubbles.includes(1) && ( +
+ } + label="Fuel Needed" + rightIcon={} + alignment="end" + /> +
+ )} +
+
+ )} + + {/* Ready to Start */} + {/* Hide during replay transition to prevent flash */} + {storyPhase === 'readyToStart' && !isReplayingRef.current && ( +
+ {/* Moon with rocket animation - centered */} +
+
+ +
+
+ + {/* Characters on same row - Rahi left, Rilo right */} +
+ {/* Captain Rahi - FIXED position on LEFT */} +
+
+ 👨‍🚀 +
+
+ + {/* Rilo - FIXED position on RIGHT - shifted left on mobile for better visibility */} +
+ {/* Speech bubble - positioned absolutely above Rilo, pointing to Rilo */} + {/* Centered above Rilo, but constrained to stay within viewport on mobile */} +
0 ? 1 : 0, + transition: 'opacity 0.5s ease-in-out', + pointerEvents: visibleSpeechBubbles.length > 0 ? 'auto' : 'none', + width: 'clamp(120px, 70vw, 320px)', + maxWidth: 'min(calc(100vw - 3rem), 320px)', + left: '50%', + transform: 'translateX(-50%)', + right: 'auto' + }} + > + {visibleSpeechBubbles.includes(0) && ( + + )} +
+ + {/* Rilo character - same vertical position as Rahi */} +
+
+ 🤖 +
+
+
+
+ + {/* Start button centered at bottom */} +
+ +
+
+ )} +
+ +
+ + + + ); +} + +export default LetterLauncherGameStoryPreview; + diff --git a/src/lib/axl-explorations/src/components/games/MemoryGame.tsx b/src/lib/axl-explorations/src/components/games/MemoryGame.tsx new file mode 100644 index 00000000..3bc0284a --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/MemoryGame.tsx @@ -0,0 +1,779 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ProgressBar } from "../ProgressBar"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { AudioButton } from "../AudioButton"; +import MemoryGamePreview from "./MemoryGamePreview"; +import { MemoryGameCore, MemoryQuestion } from "./MemoryGameCore"; +import { ArrowLeft, ArrowRight, Globe, CheckCircle, Timer, RotateCcw, TrendingUp } from "lucide-react"; +import { cn } from "../../lib/utils"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { memoryGameDataLoader, type Language as MemoryGameLanguage, type MultilingualMemoryQuestion } from "../../utils/memoryGameDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; + +// Extend the MultilingualMemoryQuestion to include MemoryQuestion properties +// Note: Using MemoryQuestion's language type (from constants) which includes 'hi' +interface ExtendedMultilingualMemoryQuestion extends Omit, MemoryQuestion {} + +interface MemoryGameProps { + onBack: () => void; +} + +export function MemoryGame({ onBack }: MemoryGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [sequences, setSequences] = useState([]); + const [currentSequenceIndex, setCurrentSequenceIndex] = useState(0); + const [score, setScore] = useState(0); + const [showSequence, setShowSequence] = useState(true); + const [userInput, setUserInput] = useState([]); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [sequenceTimer, setSequenceTimer] = useState(3); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [usedSequences, setUsedSequences] = useState>(new Set()); + const [levelFailed, setLevelFailed] = useState(false); + const [timeRemaining, setTimeRemaining] = useState(0); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [showTimeoutMessage, setShowTimeoutMessage] = useState(false); + const [currentLetterOptions, setCurrentLetterOptions] = useState([]); + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations + const getLanguageLevels = (language: Language) => { + switch (language) { + case 'te': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + case 'mr': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + default: + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + } + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `memoryChallenge_${selectedLanguage}` : 'memoryChallenge'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = selectedLanguage ? getLanguageLevels(selectedLanguage) : getLanguageLevels('en'); + + // Get all available letters for the selected language from JSON + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const allLetters = selectedLanguage ? memoryGameDataLoader.getAllLetters(supportedLanguage) : []; + + // Initialize game session and sequences when language or level is selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + // Add a small delay to ensure state reset completes first + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + // Generate unique sequences for this level/language using JSON data + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const newSequences = memoryGameDataLoader.generateMemoryQuestions( + supportedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + + // Add display property to each sequence for compatibility with MemoryGameCore + const extendedSequences = newSequences.map(seq => ({ + ...seq, + display: seq.sequence.join(' - ') + })); + + setSequences(extendedSequences); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) - for individual games only + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Extract metadata (currentLevel from backend) + if (result.metadata?.currentLevel) { + setBackendCurrentLevel(result.metadata.currentLevel); + } + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentSequenceIndex(0); + setScore(0); + setTotalCorrect(0); + setUserInput([]); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setShowSequence(true); + setSequenceTimer(3); + setTimeRemaining(0); + setIsTimerRunning(false); + setCurrentLetterOptions([]); + setShowTimeoutMessage(false); + setShowPreview(false); // Hide preview when level is selected + + // Clear any stored failed level when user navigates to a level + const failedLevelKey = `failedLevel_${gameKey}`; + localStorage.removeItem(failedLevelKey); + } + }, [selectedLevel, selectedLanguage]); + + const currentSequence = sequences[currentSequenceIndex]; + useEffect(() => { + if (currentSequence) setQuestionStartTime(Date.now()); + }, [currentSequenceIndex]); + + // Get time limit for memory sequences based on complexity and level + const getTimeLimit = (complexity: string) => { + // Base time limits by complexity for memory sequences + const baseTime = { + basic: 8, + intermediate: 6, + advanced: 5, + expert: 4, + master: 3 + }; + + // Additional level-based time reduction for higher levels + const levelBonus = Math.max(0, Math.floor((currentLevel - 1) * 0.2)); + + return Math.max(3, baseTime[complexity as keyof typeof baseTime] - levelBonus); + }; + + // Generate question-specific letter options (correct letters + distractors) + const generateQuestionLetterOptions = (sequence: string[]) => { + const correctLetters = [...sequence]; + const numDistractors = correctLetters.length; // Same number of distractors as correct letters + + // Get all available letters for the language + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const availableLetters = selectedLanguage ? memoryGameDataLoader.getAllLetters(supportedLanguage) : []; + + // Filter out correct letters to get potential distractors + const potentialDistractors = availableLetters.filter(letter => !correctLetters.includes(letter)); + + // Shuffle and select distractors + const shuffledDistractors = potentialDistractors.sort(() => Math.random() - 0.5); + const selectedDistractors = shuffledDistractors.slice(0, numDistractors); + + // Combine correct letters and distractors, then shuffle + const allOptions = [...correctLetters, ...selectedDistractors]; + return allOptions.sort(() => Math.random() - 0.5); + }; + + // Generate letter options when question changes + useEffect(() => { + if (currentSequence && !showSequence) { + const newOptions = generateQuestionLetterOptions(currentSequence.sequence); + setCurrentLetterOptions(newOptions); + } + }, [currentSequenceIndex, currentSequence, showSequence, selectedLanguage]); + + // Timer effect for memory sequence display phase + useEffect(() => { + if (currentSequence && showSequence && !showFeedback && sequences.length > 0) { + // Start timer when showing sequence + const timeLimit = getTimeLimit(difficultySettings.complexity); + setTimeRemaining(timeLimit); + setIsTimerRunning(true); + setShowTimeoutMessage(false); // Reset timeout message for new sequence + + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + // Time's up - automatically move to input phase + setIsTimerRunning(false); + setShowSequence(false); + setSequenceTimer(3); + setShowTimeoutMessage(true); // Show timeout message + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => { + clearInterval(timer); + setIsTimerRunning(false); + }; + } + }, [currentSequenceIndex, currentSequence, showSequence, showFeedback, difficultySettings.complexity, sequences.length]); + + + const addLetterToInput = (letter: string) => { + if (userInput.length < currentSequence.length) { + setUserInput([...userInput, letter]); + // Don't reset timeout message here - let it stay visible until user submits answer + } + }; + + const removeLastLetter = () => { + setUserInput(userInput.slice(0, -1)); + }; + + const checkSequence = async () => { + const correct = JSON.stringify(userInput) === JSON.stringify(currentSequence.sequence); + setIsCorrect(correct); + setShowFeedback(true); + setShowTimeoutMessage(false); + + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `memory_${currentLevel}_${currentSequenceIndex}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'memoryChallenge', + userInput.join(''), + currentSequence.sequence.join(''), + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'memoryChallenge', + userAnswer: userInput.join(''), + correctAnswer: currentSequence.sequence.join(''), + isCorrect: correct, + responseTime: responseTime, + complexity: currentSequence.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(score + 1); + setTotalCorrect(totalCorrect + 1); + } + + // Don't auto-advance - player must manually continue + }; + + const handleContinue = useCallback(async () => { + if (currentSequenceIndex < sequences.length - 1) { + setCurrentSequenceIndex(currentSequenceIndex + 1); + setUserInput([]); + setShowFeedback(false); + setShowSequence(true); + setSequenceTimer(3); + setShowTimeoutMessage(false); + } else { + // Level complete + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / sequences.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Memory Challenge Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: sequences.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + endSession(); + if (gameProgress.currentLevel < currentLevel) { + setShowLevelUp(true); + } + } + + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentSequenceIndex, sequences.length, totalCorrect, isCorrect, gameProgress.currentLevel, currentLevel]); + + + const resetGame = () => { + setCurrentSequenceIndex(0); + setScore(0); + setTotalCorrect(0); + setUserInput([]); + setShowFeedback(false); + setShowSequence(true); + setSequenceTimer(3); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setTimeRemaining(0); + setIsTimerRunning(false); + setCurrentLetterOptions([]); + setShowTimeoutMessage(false); // Reset timeout message + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + // Start new session and regenerate sequences using JSON data + if (selectedLanguage) { + const session = startSession(gameKey); + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const newSequences = memoryGameDataLoader.generateMemoryQuestions( + supportedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + + // Add display property to each sequence for compatibility with MemoryGameCore + const extendedSequences = newSequences.map(seq => ({ + ...seq, + display: seq.sequence.join(' - ') + })); + + setSequences(extendedSequences); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/memory-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/memory-game'); + }; + + const handleBack = () => { + // Navigate back to level selector + navigate('/memory-game'); + }; + + + const handleReset = () => { + // Reset the current game session + resetGame(); + }; + + const handlePlayAgain = () => { + resetGame(); + }; + + const handleNextLevel = () => { + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL + navigate(`/memory-game/level/${nextLevel}`); + + setShowLevelUp(false); + resetGame(); + }; + + const calculateStars = () => { + if (sequences.length === 0) return 0; + const percentage = (totalCorrect / sequences.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (sequences.length > 0) { + if (totalCorrect === sequences.length) { + achievements.push("Memory Master - Perfect Recall!"); + } + if (totalCorrect >= Math.floor(sequences.length * 0.8)) { + achievements.push("Brain Champion - Amazing Memory!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level selector is requested (after preview) + if (showLevelSelector) { + return ( + { + setShowPreview(true); + onBack(); + }}// Go back to preview instead of main menu + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Memory Challenge" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + + ); + } + + // Main game screen + if (!currentSequence) { + return ( +
+
+
+

Loading Memory Challenge...

+

Setting up your sequences...

+
+
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Memory Challenge +

+
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Timer - Only show during sequence display phase - Hide when time is up */} + {showSequence && !showTimeoutMessage && ( +
+
+
+ +
+
+
+ )} + + {/* Timeout Message - Show when time is up */} + {showTimeoutMessage && ( +
+
+
+ +
+ {selectedLanguage === 'te' ? 'ముగిసింది!' : + selectedLanguage === 'mr' ? 'संपला!' : + selectedLanguage === 'kn' ? 'ಮುಗಿಯಿತು!' : + "Time Up!"} +
+
+
+
+ )} + + {/* Game Area */} + +
+
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/MemoryGameCore.tsx b/src/lib/axl-explorations/src/components/games/MemoryGameCore.tsx new file mode 100644 index 00000000..90ac5977 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/MemoryGameCore.tsx @@ -0,0 +1,314 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { CheckCircle } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { ContinueButton } from "./ContinueButton"; + +export interface MemoryQuestion { + sequence: string[]; + display: string; + complexity: string; + language: Language; +} + +interface MemoryGameCoreProps { + // Question data + currentSequence: MemoryQuestion; + + // Game state + mode: 'game' | 'preview'; + selectedLanguage: Language; + currentLevel: number; + + // UI state + showSequence: boolean; + showFeedback: boolean; + isCorrect: boolean; + userInput: string[]; + currentLetterOptions: string[]; + + // Preview-specific state + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + sequenceTimer?: number; + + // Handlers + onLetterClick: (letter: string) => void; + onRemoveLast: () => void; + onCheckSequence?: () => void; + onContinue?: () => void; + showContinueButton?: boolean; // Control whether to show the continue button + + // Styling + className?: string; +} + +export function MemoryGameCore({ + currentSequence, + mode, + selectedLanguage, + currentLevel, + showSequence, + showFeedback, + isCorrect, + userInput, + currentLetterOptions, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + sequenceTimer = 0, + onLetterClick, + onRemoveLast, + onCheckSequence, + onContinue, + showContinueButton, + className = '' +}: MemoryGameCoreProps) { + const optionsRef = useRef(null); + + // Get localized text + const getLocalizedText = (key: string) => { + const texts = { + whatWasSequence: { + en: 'What was the sequence?', + te: 'క్రమం ఏమిటి?', + kn: 'ಅನುಕ್ರಮ ಏನು?', + mr: 'क्रम काय होता?' + }, + clickLetters: { + en: 'Click letters below...', + te: 'క్రింద అక్షరాలను క్లిక్ చేయండి...', + kn: 'ಕೆಳಗೆ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ...', + mr: 'खाली अक्षरांवर क्लिक करा...' + }, + removeLast: { + en: 'Remove Last', + te: 'చివరిది తొలగించు', + kn: 'ಕೊನೆಯದನ್ನು ತೆಗೆದುಹಾಕಿ', + mr: 'शेवटचे काढा' + }, + checkSequence: { + en: 'Check Sequence', + te: 'క్రమం తనిఖీ చేయండి', + kn: 'ಅನುಕ್ರಮವನ್ನು ಪರಿಶೀಲಿಸಿ', + mr: 'क्रम तपासा' + }, + check: { + en: 'Check', + te: 'తనిఖీ', + kn: 'ಪರಿಶೀಲಿಸಿ', + mr: 'तपासा' + }, + correct: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿಯಿದೆ!', + mr: '🎉 बरोबर!' + }, + wrong: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + + return texts[key as keyof typeof texts]?.[selectedLanguage] || texts[key as keyof typeof texts]?.en || ''; + }; + + return ( +
+ {showSequence ? ( + /* Sequence Display Phase */ +
+ {/* Timer - Only show during sequence display phase in preview mode */} + {isPreview && sequenceTimer > 0 && ( +
+
+
+ +
+
+
+ )} + + {/* Sequence Display */} +
+
+ {currentSequence.display} +
+
+
+ ) : ( + /* Input Phase */ +
+ + {/* User Input Display */} +
+
+ {userInput.length === 0 ? ( +

{getLocalizedText('clickLetters')}

+ ) : ( + userInput.map((letter, index) => ( +
+ {letter} +
+ )) + )} +
+ + {/* Progress indicator */} +
+ {userInput.length} of {currentSequence.sequence.length} letters +
+
+ +{/* Letter Selection Grid */} +
+
+
+ {/* Hand Pointer for preview mode */} + {mode === 'preview' && showHandPointer && !showFeedback && userInput.length < currentSequence.sequence.length && ( +
+
+ 👆 +
+
+ )} + + {/* Dynamic Responsive Grid */} +
+ {currentLetterOptions.map((letter) => ( + + + ))} +
+ +
+
+
+ + + {/* Action Buttons */} +
+ {!showFeedback && ( + + )} + + {userInput.length === currentSequence.sequence.length && !showFeedback && onCheckSequence && ( + + )} +
+
+ )} + + {/* Feedback - Fixed Height Area */} +
+ {showFeedback && ( + <> + {isCorrect ? ( +
+

+ {getLocalizedText('correct')} +

+
+ ) : ( +
+

{getLocalizedText('wrong')}

+
+ )} + + {/* Continue Button - only show in game mode, unless showContinueButton is explicitly false */} + + + )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/MemoryGamePreview.tsx b/src/lib/axl-explorations/src/components/games/MemoryGamePreview.tsx new file mode 100644 index 00000000..730e2926 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/MemoryGamePreview.tsx @@ -0,0 +1,664 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Brain, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw, Eye } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { MemoryGameCore, MemoryQuestion } from "./MemoryGameCore"; + +interface MemoryGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" button + | 'showSequence' // Show sequence for a few seconds + | 'instruction2' // After sequence, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForInput' // Show letter options, wait for user to build sequence + | 'wrongAnswer' // User built wrong sequence + | 'instruction4' // After correct sequence, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Memory Challenge", + description: "Watch, remember, and recall the sequence!", + steps: [ + "👀 Watch the letter sequence carefully", + "🧠 Remember the order of letters", + "🎯 Click letters in the correct sequence", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a sequence of letters. Click 'I'm Ready' when prepared", + instruction2: "Good! Now remember that sequence", + instruction3: "Now recreate the sequence by clicking the letters in order", + instruction4: "Perfect! You remembered the sequence correctly!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + readyButton: "I'm Ready", + checkButton: "Check Sequence", + narration1: "Get ready! You'll see a sequence of letters. Click I'm Ready when prepared", + narration2: "Good! Now remember that sequence", + narration3: "Now recreate the sequence by clicking the letters in order", + narration4: "Perfect! You remembered the sequence correctly!", + howToPlay: "How to Play", + rememberText: "Watch & Remember", + clickLettersText: "Click letters below...", + whatWasSequence: "What was the sequence?", + demo: { + sequence: ["A", "B", "C"], + options: ["A", "B", "C", "D", "E", "F"], + } + }, + te: { + title: "జ్ఞాపకశక్తి సవాలు", + description: "చూడండి, గుర్తుంచుకోండి మరియు జ్ఞాపకం చేసుకోండి!", + steps: [ + "👀 అక్షర క్రమాన్ని జాగ్రత్తగా చూడండి", + "🧠 అక్షరాల క్రమాన్ని గుర్తుంచుకోండి", + "🎯 సరైన క్రమంలో అక్షరాలను క్లిక్ చేయండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు అక్షరాల క్రమాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఆ క్రమాన్ని గుర్తుంచుకోండి", + instruction3: "ఇప్పుడు క్రమంలో అక్షరాలను క్లిక్ చేయడం ద్వారా క్రమాన్ని పునఃసృష్టించండి", + instruction4: "పర్ఫెక్ట్! మీరు క్రమాన్ని సరిగ్గా గుర్తుంచుకున్నారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + checkButton: "క్రమం తనిఖీ చేయండి", + narration1: "సిద్ధంగా ఉండండి! మీరు అక్షరాల క్రమాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఆ క్రమాన్ని గుర్తుంచుకోండి", + narration3: "ఇప్పుడు క్రమంలో అక్షరాలను క్లిక్ చేయడం ద్వారా క్రమాన్ని పునఃసృష్టించండి", + narration4: "పర్ఫెక్ట్! మీరు క్రమాన్ని సరిగ్గా గుర్తుంచుకున్నారు!", + howToPlay: "ఎలా ఆడాలి", + rememberText: "చూడండి & గుర్తుంచుకోండి", + clickLettersText: "క్రింద అక్షరాలను క్లిక్ చేయండి...", + whatWasSequence: "క్రమం ఏమిటి?", + demo: { + sequence: ["అ", "ఆ", "ఇ"], + options: ["అ", "ఆ", "ఇ", "ఈ", "ఉ", "ఊ"], + } + }, + kn: { + title: "ಸ್ಮೃತಿ ಸವಾಲು", + description: "ನೋಡಿ, ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ ಮತ್ತು ನೆನಪಿಸಿಕೊಳ್ಳಿ!", + steps: [ + "👀 ಅಕ್ಷರ ಅನುಕ್ರಮವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + "🧠 ಅಕ್ಷರಗಳ ಕ್ರಮವನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + "🎯 ಸರಿಯಾದ ಅನುಕ್ರಮದಲ್ಲಿ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ತಯಾರಾಗಿ! ನೀವು ಅಕ್ಷರಗಳ ಅನುಕ್ರಮವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಆ ಅನುಕ್ರಮವನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + instruction3: "ಈಗ ಕ್ರಮದಲ್ಲಿ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡುವ ಮೂಲಕ ಅನುಕ್ರಮವನ್ನು ಮರುಸೃಷ್ಟಿಸಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಅನುಕ್ರಮವನ್ನು ಸರಿಯಾಗಿ ನೆನಪಿಟ್ಟುಕೊಂಡಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + readyButton: "ನಾನು ಸಿದ್ಧ", + checkButton: "ಅನುಕ್ರಮವನ್ನು ಪರಿಶೀಲಿಸಿ", + narration1: "ತಯಾರಾಗಿ! ನೀವು ಅಕ್ಷರಗಳ ಅನುಕ್ರಮವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಆ ಅನುಕ್ರಮವನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + narration3: "ಈಗ ಕ್ರಮದಲ್ಲಿ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡುವ ಮೂಲಕ ಅನುಕ್ರಮವನ್ನು ಮರುಸೃಷ್ಟಿಸಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಅನುಕ್ರಮವನ್ನು ಸರಿಯಾಗಿ ನೆನಪಿಟ್ಟುಕೊಂಡಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + rememberText: "ನೋಡಿ & ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + clickLettersText: "ಕೆಳಗೆ ಅಕ್ಷರಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ...", + whatWasSequence: "ಅನುಕ್ರಮ ಏನು?", + demo: { + sequence: ["ಅ", "ಆ", "ಇ"], + options: ["ಅ", "ಆ", "ಇ", "ಈ", "ಉ", "ಊ"], + } + }, + mr: { + title: "स्मृती आव्हान", + description: "पहा, लक्षात ठेवा आणि आठवा!", + steps: [ + "👀 अक्षर अनुक्रम काळजीपूर्वक पहा", + "🧠 अक्षरांचा क्रम लक्षात ठेवा", + "🎯 योग्य क्रमाने अक्षरांवर क्लिक करा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "तयार व्हा! तुम्हाला अक्षरांचा क्रम दिसेल. तयार असाल तेव्हा 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता तो क्रम लक्षात ठेवा", + instruction3: "आता क्रमाने अक्षरांवर क्लिक करून क्रम पुन्हा तयार करा", + instruction4: "उत्कृष्ट! तुम्ही क्रम योग्यरित्या लक्षात ठेवला!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + readyButton: "मी तयार आहे", + checkButton: "क्रम तपासा", + narration1: "तयार व्हा! तुम्हाला अक्षरांचा क्रम दिसेल. तयार असाल तेव्हा मी तयार आहे क्लिक करा", + narration2: "चांगले! आता तो क्रम लक्षात ठेवा", + narration3: "आता क्रमाने अक्षरांवर क्लिक करून क्रम पुन्हा तयार करा", + narration4: "उत्कृष्ट! तुम्ही क्रम योग्यरित्या लक्षात ठेवला!", + howToPlay: "कसे खेळायचे", + rememberText: "पहा & लक्षात ठेवा", + clickLettersText: "खाली अक्षरांवर क्लिक करा...", + whatWasSequence: "क्रम काय होता?", + demo: { + sequence: ["अ", "आ", "इ"], + options: ["अ", "आ", "इ", "ई", "उ", "ऊ"], + } + } +}; + +export function MemoryGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: MemoryGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [userSequence, setUserSequence] = useState([]); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showSequence, setShowSequence] = useState(false); + const [sequenceTimer, setSequenceTimer] = useState(3); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question object for MemoryGameCore + const demoQuestion: MemoryQuestion = { + sequence: instructions.demo.sequence, + display: instructions.demo.sequence.join(' - '), + complexity: 'easy', + language: contentLanguage + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined letter games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined letter games audio files for Memory Challenge + const gameName = 'Combined Letter Games'; + const subGame = 'Memory Challenge'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + case 'showSequence': + setCurrentStep(0); + break; + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForInput': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setUserSequence([]); + setShowFeedback(false); + setShowSequence(false); + setSequenceTimer(3); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Sequence countdown timer + useEffect(() => { + if (showSequence && sequenceTimer > 0) { + const timer = setInterval(() => { + setSequenceTimer(prev => { + if (prev <= 1) { + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } + }, [showSequence, sequenceTimer]); + + // When sequence timer ends, move to instruction 2 and show input area (disabled) + useEffect(() => { + if (showSequence && sequenceTimer === 0) { + const handleSequenceEnd = async () => { + setShowSequence(false); + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + setDemoStep('waitForInput'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }; + + handleSequenceEnd(); + } + }, [showSequence, sequenceTimer]); + + // Handle ready button click + const handleReadyClick = () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + setDemoStep('showSequence'); + setShowSequence(true); + setSequenceTimer(3); + }; + + // Handle letter click to build sequence + const handleLetterClick = (letter: string) => { + if (demoStep !== 'waitForInput' || showFeedback) return; + + if (userSequence.length < instructions.demo.sequence.length) { + setUserSequence(prev => [...prev, letter]); + } + }; + + // Handle check sequence button click + const handleCheckSequence = async () => { + if (userSequence.length !== instructions.demo.sequence.length || showFeedback) return; + + setShowFeedback(true); + + const isCorrect = JSON.stringify(userSequence) === JSON.stringify(instructions.demo.sequence); + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setUserSequence([]); + }, 2000); + } + }; + + // Remove last letter from sequence + const handleRemoveLetter = () => { + if (userSequence.length > 0 && !showFeedback) { + setUserSequence(prev => prev.slice(0, -1)); + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setUserSequence([]); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowSequence(false); + setSequenceTimer(3); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showInputArea = demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForInput' || demoStep === 'instruction4' || demoStep === 'complete'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Handle back button with audio cleanup + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Handle start game with audio cleanup + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Demo Content - Show sequentially */} +
+ {/* Step 1: Ready Button Section */} + {showReadyButton && !showSequence && !showInputArea && ( +
+
+ +
+ + {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {/* Step 2: Sequence Display - Use MemoryGameCore for consistency */} + {showSequence && !showInputArea && ( +
+ {}} + onRemoveLast={() => {}} + onCheckSequence={() => {}} + className="w-full" + /> +
+ )} + + {/* Step 3: Input Area */} + {showInputArea && ( + + )} +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} + +export default MemoryGamePreview; diff --git a/src/lib/axl-explorations/src/components/games/PhonemeGame.tsx b/src/lib/axl-explorations/src/components/games/PhonemeGame.tsx new file mode 100644 index 00000000..8cdae5f5 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/PhonemeGame.tsx @@ -0,0 +1,267 @@ +import { useState, useEffect } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { AudioButton } from "../AudioButton"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { ArrowLeft, RotateCcw, TrendingUp } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { generatePhonemeQuestions, type PhonemeQuestion } from "../../utils/gameDataGenerators"; + +interface PhonemeGameProps { + onBack: () => void; +} + +export function PhonemeGame({ onBack }: PhonemeGameProps) { + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + currentSession + } = useLearningProgress(); + + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedPhoneme, setSelectedPhoneme] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + + const gameProgress = getGameProgress('phoneme'); + const difficultySettings = getDifficultySettings('phoneme', gameProgress.currentLevel); + + // Initialize game session and questions + useEffect(() => { + const session = startSession('phoneme'); + setPreviousLevel(gameProgress.currentLevel); + + const newQuestions = generatePhonemeQuestions( + gameProgress.currentLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + }, []); + + const currentQuestion = questions[currentQuestionIndex]; + + const handlePhonemeSelect = (phoneme: string) => { + if (showFeedback) return; + + setSelectedPhoneme(phoneme); + const correct = phoneme === currentQuestion.target; + setIsCorrect(correct); + setShowFeedback(true); + + // Record the answer for adaptive learning + recordAnswer(correct); + + if (correct) { + setScore(score + 10); + setTotalCorrect(totalCorrect + 1); + } + + setTimeout(() => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedPhoneme(null); + setShowFeedback(false); + } else { + // Game complete - end session and check for level up + endSession(); + const newProgress = getGameProgress('phoneme'); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + setIsGameComplete(true); + } + }, 1500); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedPhoneme(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + + // Start new session and regenerate questions + const session = startSession('phoneme'); + const newQuestions = generatePhonemeQuestions( + gameProgress.currentLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + }; + + const calculateStars = () => { + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (totalCorrect === questions.length) { + achievements.push("Phoneme Master - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Sound Explorer - Great Progress!"); + } + if (showLevelUp) { + achievements.push(`Level Up! Now at Level ${gameProgress.currentLevel}`); + } + return achievements; + }; + + // Show success screen when game is complete + if (isGameComplete) { + return ( + { + resetGame(); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + return ( +
+ {/* Decorative elements */} +
+
🔤
+
📢
+
🎵
+
🗣️
+
+ +
+ {/* Header */} +
+ + +
+

+ Phoneme Recognition +

+
+ + Level {gameProgress.currentLevel} • {difficultySettings.complexity} +
+
+ + {/* */} +
+ + {/* Progress */} + + + + + {/* Game Area */} + +
+ +

+ {currentQuestion.audio} +

+

+ Sound: {currentQuestion.sound} +

+
+ + {/* Phoneme Options */} +
+ {currentQuestion.options.map((phoneme) => ( + + ))} +
+ + {/* Feedback */} + {showFeedback && ( +
+ {isCorrect ? ( +
+ 🎉 Excellent! That's the sound {currentQuestion.sound} +
+ ) : ( +
+ The correct sound is {currentQuestion.sound} ({currentQuestion.target}) +
+ )} +
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/ROARPhonemeGame.tsx b/src/lib/axl-explorations/src/components/games/ROARPhonemeGame.tsx new file mode 100644 index 00000000..3052deef --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARPhonemeGame.tsx @@ -0,0 +1,1194 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { AudioButton } from "../AudioButton"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import ROARPhonemeGamePreview from "./ROARPhonemeGamePreview"; +import { ROARPhonemeGameCore, type ROARPhonemeQuestion } from "./ROARPhonemeGameCore"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Volume2, Globe, Sparkles } from "lucide-react"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { soundMatchDataLoader, Complexity } from "../../utils/soundMatchDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; + +interface ROARPhonemeGameProps { + onBack: () => void; +} + +export function ROARPhonemeGame({ onBack }: ROARPhonemeGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedOption, setSelectedOption] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + + // ✅ QUESTION TRACKING: Track used questions to prevent repetition across levels + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations using JSON data + const getLanguageLevels = (language: Language) => { + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const maxLevels = soundMatchDataLoader.getMaxLevelForLanguage(supportedLanguage); + return { maxLevels }; + }; + + const languageLevels = selectedLanguage ? getLanguageLevels(selectedLanguage) : { maxLevels: 10 }; + const gameKey = selectedLanguage ? `soundMatch_${selectedLanguage}` : 'soundMatch'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel !== null ? selectedLevel : gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + + // Debug level calculation + console.log('🔍 Level calculation:', { + selectedLevel, + gameProgressCurrentLevel: gameProgress.currentLevel, + calculatedCurrentLevel: currentLevel + }); + + // Enhanced audio function for different languages with improved voice selection + const playAudio = (text: string, language: Language) => { + // Cancel any ongoing speech to prevent overlapping + speechSynthesis.cancel(); + + // Small delay to ensure cancellation is complete + setTimeout(() => { + const utterance = new SpeechSynthesisUtterance(text); + + // Natural speech settings for clear pronunciation + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 1.0; // Normal natural speed + utterance.pitch = 1.0; // Natural pitch + utterance.volume = 1.0; // Clear volume + break; + case 'kn': + utterance.lang = 'kn-IN'; + utterance.rate = 1.0; // Normal natural speed + utterance.pitch = 1.0; // Natural pitch + utterance.volume = 1.0; // Clear volume + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 1.0; // Normal natural speed + utterance.pitch = 1.0; // Natural pitch + utterance.volume = 1.0; // Clear volume + break; + default: + utterance.lang = 'en-US'; + utterance.rate = 0.9; + utterance.pitch = 1.0; + utterance.volume = 0.9; + } + + // Simplified voice selection to prevent duplicate audio + const voices = speechSynthesis.getVoices(); + let selectedVoice = null; + + if (language === 'te') { + selectedVoice = + voices.find(voice => voice.lang === 'te-IN' || voice.lang === 'te') || + voices.find(voice => voice.lang === 'hi-IN' || voice.lang === 'hi') || + voices[0]; + } else if (language === 'mr') { + selectedVoice = + voices.find(voice => voice.lang === 'mr-IN' || voice.lang === 'mr') || + voices.find(voice => voice.lang === 'hi-IN' || voice.lang === 'hi') || + voices[0]; + } else { + selectedVoice = + voices.find(voice => voice.lang === 'en-US') || + voices.find(voice => voice.lang.startsWith('en')) || + voices[0]; + } + + if (selectedVoice) { + utterance.voice = selectedVoice; + } + + speechSynthesis.speak(utterance); + }, 50); + }; + + + + // Initialize game session and questions when language/level selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + // ✅ QUESTION TRACKING: Generate questions using tracking system + console.log(`🎯 Generating questions for ${selectedLanguage} level ${currentLevel}`); + const newQuestions = generateROARPhonemeQuestionsWithTracking( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + + console.log(`📊 Generated ${newQuestions.length} questions`); + if (newQuestions.length === 0) { + console.error('❌ No questions generated! Trying fallback method...'); + // Fallback to original method + const fallbackQuestions = generateROARPhonemeQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() + ); + console.log(`📊 Fallback generated ${fallbackQuestions.length} questions`); + setQuestions(fallbackQuestions); + } else { + setQuestions(newQuestions); + } + + // ✅ QUESTION TRACKING: Update used questions to prevent repetition (only target and correct answer) + const newUsedQuestions = new Set(); + newQuestions.forEach(q => { + newUsedQuestions.add(q.target.word); + // Find the correct answer (same phoneme as target) and mark it as used + const correctAnswer = q.options.find(opt => opt.phoneme === q.target.phoneme && opt.word !== q.target.word); + if (correctAnswer) { + newUsedQuestions.add(correctAnswer.word); + } + }); + setUsedQuestions(newUsedQuestions); + + console.log(`🎮 Generated ${newQuestions.length} fresh questions for ${selectedLanguage} level ${currentLevel}`); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute backendCurrentLevel from tracking progress (score > 0 or completed advances next) + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + const computedNextLevel = Math.min(Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), languageLevels.maxLevels); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min(Math.max(computedNextLevel, backendProvided), languageLevels.maxLevels); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowPreview(false); // Hide preview when level is selected + setShowLevelUp(false); + setLevelFailed(false); + setUsedQuestions(new Set()); + } + }, [selectedLevel, selectedLanguage]); + + // Load available voices when component mounts + useEffect(() => { + const loadVoices = () => { + const voices = speechSynthesis.getVoices(); + console.log('Available voices:', voices.length); + }; + + // Load voices immediately + loadVoices(); + + // Set up voice change listener + if (speechSynthesis.onvoiceschanged !== undefined) { + speechSynthesis.onvoiceschanged = loadVoices; + } + + return () => { + speechSynthesis.cancel(); + }; + }, []); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + // Use the global child-friendly getQuestionText function defined at the bottom of the file + + // Auto-play audio when question changes (only during actual gameplay) + useEffect(() => { + console.log('🔊 Auto-play check:', { + currentQuestion: !!currentQuestion, + showFeedback, + selectedLanguage, + selectedLevel, + questionsLength: questions.length, + isGameComplete, + currentLevel, + currentQuestionIndex + }); + + if (currentQuestion && !showFeedback && selectedLanguage && selectedLevel && questions.length > 0 && !isGameComplete) { + console.log(`🔊 Auto-playing: ${currentQuestion.target.word} for ${selectedLanguage}`); + // Small delay to ensure component is rendered + const timer = setTimeout(() => { + playAudio(currentQuestion.target.word, selectedLanguage); + }, 500); + + return () => clearTimeout(timer); + } else { + console.log('🔊 Auto-play skipped - conditions not met'); + } + }, [currentQuestionIndex, currentQuestion, showFeedback, selectedLanguage, selectedLevel, questions.length, isGameComplete, currentLevel]); // ✅ Added currentLevel dependency + + // Cleanup speech synthesis on component unmount + useEffect(() => { + return () => { + speechSynthesis.cancel(); + }; + }, []); + + const handleOptionSelect = async (optionWord: string) => { + if (showFeedback) return; + + setSelectedOption(optionWord); + const selectedOptionData = currentQuestion.options.find(opt => opt.word === optionWord); + const correct = selectedOptionData?.phoneme === currentQuestion.target.phoneme; + setIsCorrect(correct); + setShowFeedback(true); + + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `soundmatch_${currentLevel}_${currentQuestionIndex}`; + // Find the correct answer - the word from options that matches the target phoneme + const correctOption = currentQuestion.options.find(opt => opt.phoneme === currentQuestion.target.phoneme); + const correctAnswerForTelemetry = correctOption?.word || currentQuestion.target.word; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'soundMatch', + optionWord, + correctAnswerForTelemetry, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'soundMatch', + userAnswer: optionWord, + correctAnswer: correctAnswerForTelemetry, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prev => prev + 1); + setTotalCorrect(prev => prev + 1); + } + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedOption(null); + setShowFeedback(false); + } else { + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Sound Match Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const handleBackClick = async () => { + await sessionTelemetryManager.endSubSessionWithBackButton(); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + // ✅ UNIQUE QUESTIONS: Reset used questions cache + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage && selectedLevel !== null) { + // Start new session and regenerate questions + const session = startSession(gameKey); + + // ✅ TRY AGAIN FIX: Reset question tracking for this level to allow retry + const complexityLevel = soundMatchDataLoader.getComplexityForLevel(currentLevel); + console.log(`🔄 Resetting game for ${selectedLanguage} level ${currentLevel} (${complexityLevel})`); + + // Reset tracking for this specific level to allow retry + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + soundMatchDataLoader.resetQuestionTracking(supportedLanguage); + + // ✅ QUESTION TRACKING: Generate questions using tracking system with fallback + const newQuestions = generateROARPhonemeQuestionsWithTracking( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + + console.log(`📊 Reset generated ${newQuestions.length} questions`); + if (newQuestions.length === 0) { + console.error('❌ No questions generated on reset! Trying fallback method...'); + // Fallback to original method + const fallbackQuestions = generateROARPhonemeQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() + ); + console.log(`📊 Fallback generated ${fallbackQuestions.length} questions`); + setQuestions(fallbackQuestions); + } else { + setQuestions(newQuestions); + } + + // ✅ QUESTION TRACKING: Update used questions to prevent repetition + const newUsedQuestions = new Set(); + const questionsToUse = newQuestions.length > 0 ? newQuestions : generateROARPhonemeQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() + ); + questionsToUse.forEach(q => { + newUsedQuestions.add(q.target.word); + q.options.forEach(opt => newUsedQuestions.add(opt.word)); + }); + setUsedQuestions(newUsedQuestions); + + console.log(`🎮 Reset complete: ${questionsToUse.length} fresh questions for ${selectedLanguage} level ${currentLevel}`); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/roar-phoneme-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/roar-phoneme-game'); + }; + + const getAvailableLevels = () => { + // ✅ ALL LEVELS OPEN: Return all levels as available + return Array.from({ length: languageLevels.maxLevels }, (_, i) => i + 1); + }; + + const calculateStars = () => { + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (totalCorrect === questions.length) { + const perfectMessage = selectedLanguage === 'te' ? + 'ఫోనీమ్ మాస్టర్ - పరిపూర్ణ శబ్ద గుర్తింపు!' : + selectedLanguage === 'mr' ? + 'फोनीम मास्टर - परिपूर्ण आवाज ओळख!' : + 'Phoneme Master - Perfect Sound Recognition!'; + achievements.push(perfectMessage); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + const detectiveMessage = selectedLanguage === 'te' ? + 'శబ్ద డిటెక్టివ్ - అద్భుత ఫోనీమ్ కౌశల్యాలు!' : + selectedLanguage === 'mr' ? + 'आवाज डिटेक्टिव् - उत्तम फोनीम कौशल्य!' : + 'Sound Detective - Great Phoneme Skills!'; + achievements.push(detectiveMessage); + } + if (showLevelUp) { + const levelUpMessage = selectedLanguage === 'te' ? + `లెవల్ అప్! ఇప్పుడు లెవల్ ${gameProgress.currentLevel}` : + selectedLanguage === 'mr' ? + `लेव्हल अप! आता लेव्हल ${gameProgress.currentLevel}` : + `Level Up! Now at Level ${gameProgress.currentLevel}`; + achievements.push(levelUpMessage); + } + return achievements; + }; + + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level not selected (after preview) + if (showLevelSelector) { + const availableLevels = getAvailableLevels(); + + return ( + { + setShowPreview(true); + onBack(); + }} // Go back to preview instead of main menu + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Sound Match" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + { + // ✅ MANUAL LEVEL ADVANCEMENT: Force advance to next level when user clicks "Next Level" + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage} (max: ${languageLevels.maxLevels})`); + + // Manually advance the level using the learning progress hook + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL + navigate(`/roar-phoneme-game/level/${nextLevel}`); + + console.log(`✅ Level advancement: selectedLevel updated to ${nextLevel}`); + + // Reset game state for new level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + // ✅ SOUND FIX: Clear used questions to ensure fresh questions for new level + setUsedQuestions(new Set()); + + // ✅ NEXT LEVEL FIX: Generate new questions for the next level + if (selectedLanguage && selectedLevel !== null) { + console.log(`🎯 Generating questions for next level ${nextLevel}`); + + // Get difficulty settings for the next level + const nextLevelDifficultySettings = getDifficultySettings(gameKey, nextLevel); + + // Generate questions for the next level + const newQuestions = generateROARPhonemeQuestionsWithTracking( + selectedLanguage, + nextLevel, + nextLevelDifficultySettings.complexity, + 10 + ); + + console.log(`📊 Next level generated ${newQuestions.length} questions`); + if (newQuestions.length === 0) { + console.error('❌ No questions generated for next level! Trying fallback method...'); + // Fallback to original method + const fallbackQuestions = generateROARPhonemeQuestions( + selectedLanguage, + nextLevel, + nextLevelDifficultySettings.complexity, + 10, + new Set() + ); + console.log(`📊 Fallback generated ${fallbackQuestions.length} questions`); + setQuestions(fallbackQuestions); + } else { + setQuestions(newQuestions); + } + + // Update used questions for the new level + const newUsedQuestions = new Set(); + const questionsToUse = newQuestions.length > 0 ? newQuestions : generateROARPhonemeQuestions( + selectedLanguage, + nextLevel, + nextLevelDifficultySettings.complexity, + 10, + new Set() + ); + questionsToUse.forEach(q => { + newUsedQuestions.add(q.target.word); + q.options.forEach(opt => newUsedQuestions.add(opt.word)); + }); + setUsedQuestions(newUsedQuestions); + + console.log(`🎮 Next level complete: ${questionsToUse.length} fresh questions for ${selectedLanguage} level ${nextLevel}`); + } + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + console.log('🔍 Debug: No current question found'); + console.log('🔍 Debug: questions.length =', questions.length); + console.log('🔍 Debug: currentQuestionIndex =', currentQuestionIndex); + console.log('🔍 Debug: selectedLanguage =', selectedLanguage); + console.log('🔍 Debug: selectedLevel =', selectedLevel); + console.log('🔍 Debug: currentLevel =', currentLevel); + console.log('🔍 Debug: levelFailed =', levelFailed); + console.log('🔍 Debug: isGameComplete =', isGameComplete); + + return ( +
+
+ {questions.length === 0 ? 'Generating questions...' : 'Loading question...'} +
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Sound Match +

+
+ + + Level {currentLevel} • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ +
+
+
+
+ ); +} + +// ✅ JSON-BASED: Generate unique ROAR Phoneme questions using JSON data +// ✅ QUESTION TRACKING: Enhanced question generation with tracking system +function generateROARPhonemeQuestionsWithTracking(language: Language, level: number, complexity: string, count: number = 10): ROARPhonemeQuestion[] { + // ✅ INPUT VALIDATION: Validate parameters + if (!language || !complexity || count <= 0) { + console.error('❌ Invalid input parameters for question generation'); + return []; + } + + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // ✅ LEVEL VALIDATION: Check if level is valid for the language + if (!soundMatchDataLoader.isValidLevel(supportedLanguage, level)) { + console.error(`❌ Invalid level ${level} for language ${language}`); + return []; + } + + // Get complexity level from JSON data + const complexityLevel = soundMatchDataLoader.getComplexityForLevel(level); + + // ✅ QUESTION TRACKING: Get unused questions for this level + const unusedItems = soundMatchDataLoader.getUnusedQuestionsForLevel(supportedLanguage, complexityLevel); + + // ✅ VALIDATE DATA: Ensure we have enough vocabulary + if (!unusedItems || unusedItems.length === 0) { + console.error(`❌ No unused vocabulary found for ${language} complexity ${complexityLevel}`); + return []; + } + + // ✅ AVAILABILITY CHECK: Check if we have enough questions + if (unusedItems.length < count) { + console.warn(`⚠️ Only ${unusedItems.length} unused questions available, requested ${count}`); + // Reset tracking for this level if we run out of questions + soundMatchDataLoader.resetQuestionTracking(supportedLanguage); + const freshItems = soundMatchDataLoader.getItemsForComplexity(supportedLanguage, complexityLevel); + if (freshItems.length >= count) { + console.log(`🔄 Reset tracking for ${language} ${complexityLevel} level`); + return generateROARPhonemeQuestionsWithTracking(language, level, complexity, count); + } + } + + const allItems = soundMatchDataLoader.getAllItemsForLanguage(supportedLanguage); + const questions: ROARPhonemeQuestion[] = []; + + console.log(`🎯 Generating questions for ${language} level ${level} (${complexityLevel})`); + console.log(`📊 Available unused items: ${unusedItems.length}, Requested: ${count}`); + + for (let i = 0; i < Math.min(count, unusedItems.length); i++) { + const targetItem = unusedItems[i]; + + // ✅ QUESTION TRACKING: Mark this question as used + soundMatchDataLoader.markQuestionAsUsed(supportedLanguage, complexityLevel, targetItem.word); + + // Find items with the same phoneme for correct answers + const samePhonemeItems = soundMatchDataLoader.findItemsWithSamePhoneme( + supportedLanguage, + targetItem.phoneme, + [targetItem.word] + ); + + // Find items with different phonemes for distractors + const differentPhonemeItems = soundMatchDataLoader.findItemsWithDifferentPhoneme( + supportedLanguage, + targetItem.phoneme, + [targetItem.word] + ); + + // Create options array + const options = []; + + // Add the target as the correct answer + options.push({ + image: targetItem.image, + word: targetItem.word, + phoneme: targetItem.phoneme + }); + + // Add 3 distractors with different phonemes + const shuffledDistractors = [...differentPhonemeItems].sort(() => Math.random() - 0.5); + for (let j = 0; j < 3 && j < shuffledDistractors.length; j++) { + const distractor = shuffledDistractors[j]; + options.push({ + image: distractor.image, + word: distractor.word, + phoneme: distractor.phoneme + }); + } + + // Shuffle options so correct answer isn't always first + const shuffledOptions = options.sort(() => Math.random() - 0.5); + + const question: ROARPhonemeQuestion = { + target: { + image: targetItem.image, + word: targetItem.word, + phoneme: targetItem.phoneme + }, + options: shuffledOptions, + audio: targetItem.word, + complexity: complexityLevel + }; + + // ✅ VALIDATION: Validate the generated question + const validation = soundMatchDataLoader.validateQuestion(question); + if (validation.isValid) { + questions.push(question); + console.log(`✅ Generated question ${i + 1}: ${targetItem.word} (${targetItem.phoneme})`); + } else { + console.error(`❌ Invalid question generated:`, validation.errors); + } + } + + console.log(`🎉 Successfully generated ${questions.length} questions for ${language} level ${level}`); + return questions; +} + +function generateROARPhonemeQuestions(language: Language, level: number, complexity: string, count: number = 10, usedQuestions: Set = new Set()): ROARPhonemeQuestion[] { + // ✅ INPUT VALIDATION: Validate parameters + if (!language || !complexity || count <= 0) { + console.error('❌ Invalid input parameters for question generation'); + return []; + } + + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + + // ✅ LEVEL VALIDATION: Check if level is valid for the language + if (!soundMatchDataLoader.isValidLevel(supportedLanguage, level)) { + console.error(`❌ Invalid level ${level} for language ${language}`); + return []; + } + + // Get complexity level from JSON data + const complexityLevel = soundMatchDataLoader.getComplexityForLevel(level); + const complexityItems = soundMatchDataLoader.getItemsForComplexity(supportedLanguage, complexityLevel); + const allItems = soundMatchDataLoader.getAllItemsForLanguage(supportedLanguage); + + // ✅ VALIDATE DATA: Ensure we have enough vocabulary + if (!complexityItems || complexityItems.length === 0) { + console.error(`❌ No vocabulary found for ${language} complexity ${complexityLevel}`); + return []; + } + + console.log(`🎯 Generating questions for ${language} level ${level} with complexity: ${complexityLevel}`); + console.log(`📊 Available words in ${complexityLevel}: ${complexityItems.length}`); + + // ✅ UNIQUE QUESTIONS: Filter out words already used in current session + const availableItems = soundMatchDataLoader.filterUnusedItems(complexityItems, usedQuestions); + + // If we don't have enough unique words, reset the cache for this complexity level + if (availableItems.length < count) { + console.log(`Resetting question cache for ${language} ${complexityLevel} - only ${availableItems.length} unique words remaining`); + + // Remove only this complexity level's words from used questions + const complexityWords = complexityItems.map(item => item.word); + complexityWords.forEach(word => usedQuestions.delete(word)); + + // Refresh available items + const refreshedItems = soundMatchDataLoader.filterUnusedItems(complexityItems, usedQuestions); + if (refreshedItems.length >= count) { + console.log(`Cache reset successful - now have ${refreshedItems.length} unique words`); + } + } + + const questions: ROARPhonemeQuestion[] = []; + const usedTargets: string[] = []; + + let questionsGenerated = 0; + let maxAttempts = count * 3; // Allow more attempts to find valid targets + + while (questionsGenerated < count && maxAttempts > 0) { + maxAttempts--; + + // ✅ INFINITE LOOP PREVENTION: Get a random target item that hasn't been used + let target; + let attempts = 0; + const maxTargetAttempts = 20; + + // Use available items first, then fall back to all items + const targetPool = availableItems.length > 0 ? availableItems : complexityItems; + + do { + target = targetPool[Math.floor(Math.random() * targetPool.length)]; + attempts++; + } while (usedTargets.includes(target.phoneme) && attempts < maxTargetAttempts); + + if (attempts >= maxTargetAttempts) { + console.warn(`Could not find unique target after ${maxTargetAttempts} attempts`); + // Use any available target as fallback + target = targetPool[0]; + } + + usedTargets.push(target.phoneme); + + // Create options WITHOUT the target (only 3 distractors + 1 same sound item) + let options = []; + + // Find ONE item that starts with the same sound as target + // Ensure only supported languages are passed (soundMatchDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const sameSoundItems = soundMatchDataLoader.findItemsWithSamePhoneme(supportedLanguage, target.phoneme, [target.word]); + + // CRITICAL: Ensure we always have exactly one correct answer + if (sameSoundItems.length > 0) { + const sameSoundItem = sameSoundItems[Math.floor(Math.random() * sameSoundItems.length)]; + options.push(sameSoundItem); + } else { + // If no same-sound item found, log warning and skip this target + console.warn(`No same-sound item found for "${target.word}" with phoneme "${target.phoneme}". Skipping this target.`); + continue; // Skip to next iteration + } + + // Add distractors with different phonemes to fill remaining slots + // Prioritize distractors from the same complexity level for better difficulty scaling + const sameLevelDistractors = complexityItems.filter(item => + item.phoneme !== target.phoneme && + item.word !== target.word && + !options.find(opt => opt.word === item.word) && + !options.find(opt => opt.image === item.image) // Check for duplicate images + ); + + const otherLevelDistractors = allItems.filter(item => + item.phoneme !== target.phoneme && + item.word !== target.word && + !options.find(opt => opt.word === item.word) && + !options.find(opt => opt.image === item.image) && // Check for duplicate images + !sameLevelDistractors.find(opt => opt.word === item.word) + ); + + // First add same-level distractors, then other levels if needed + let allDistractors = [...sameLevelDistractors, ...otherLevelDistractors]; + + // Add random distractors to make total 4 options + while (options.length < 4 && allDistractors.length > 0) { + const randomIndex = Math.floor(Math.random() * allDistractors.length); + const randomDistractor = allDistractors[randomIndex]; + if (!options.find(opt => opt.word === randomDistractor.word) && + !options.find(opt => opt.image === randomDistractor.image)) { // Check for duplicate images + options.push(randomDistractor); + } + allDistractors.splice(randomIndex, 1); // Remove used distractor + } + + // VALIDATION: Ensure we have exactly 4 options and 1 correct answer + if (options.length !== 4) { + console.warn(`Question ${questionsGenerated + 1}: Only ${options.length} options generated for target "${target.word}"`); + // Fill remaining slots with any available items + while (options.length < 4) { + const fallbackItem = allItems.find(item => + !options.find(opt => opt.word === item.word) + ); + if (fallbackItem) { + options.push(fallbackItem); + } else { + break; // Prevent infinite loop + } + } + } + + // Verify we have exactly one correct answer + const correctAnswers = options.filter(opt => opt.phoneme === target.phoneme); + if (correctAnswers.length !== 1) { + console.error(`Question ${questionsGenerated + 1}: ${correctAnswers.length} correct answers found for target "${target.word}"`); + } + + // Verify no duplicate images in options + const imageCounts: Record = {}; + options.forEach(opt => { + imageCounts[opt.image] = (imageCounts[opt.image] || 0) + 1; + }); + + const duplicateImages = Object.entries(imageCounts).filter(([image, count]) => count > 1); + if (duplicateImages.length > 0) { + console.error(`Question ${questionsGenerated + 1}: Duplicate images found:`, duplicateImages); + } + + // Shuffle options + options = options.sort(() => Math.random() - 0.5); + + questions.push({ + target, + options, + audio: getTargetWordText(target.word), + complexity + }); + + // ✅ TRACK USAGE: Only mark target and correct answer as used (allow distractors to be reused) + usedQuestions.add(target.word); + // Find the correct answer (same phoneme as target) and mark it as used + const correctAnswer = options.find(opt => opt.phoneme === target.phoneme && opt.word !== target.word); + if (correctAnswer) { + usedQuestions.add(correctAnswer.word); + } + + questionsGenerated++; + } + + return questions; +} + +// Helper function to get target word for pronunciation +function getTargetWordText(targetWord: string): string { + return targetWord; // Just the word for clear pronunciation +} diff --git a/src/lib/axl-explorations/src/components/games/ROARPhonemeGameCore.tsx b/src/lib/axl-explorations/src/components/games/ROARPhonemeGameCore.tsx new file mode 100644 index 00000000..70253c84 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARPhonemeGameCore.tsx @@ -0,0 +1,266 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { Volume2 } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { ContinueButton } from "./ContinueButton"; +import { attachSlowLoadToast } from "../../utils/audioUtils"; + +export interface ROARPhonemeQuestion { + target: { + image: string; + word: string; + phoneme: string; + }; + options: { + image: string; + word: string; + phoneme: string; + }[]; + audio: string; + complexity: string; +} + +export interface ROARPhonemeGameCoreProps { + currentQuestion: ROARPhonemeQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + showFeedback?: boolean; + isCorrect?: boolean; + selectedOption?: string | null; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onOptionSelect: (optionWord: string) => void; + onContinue?: () => void; + className?: string; +} + +export function ROARPhonemeGameCore({ + currentQuestion, + mode, + selectedLanguage, + showFeedback = false, + isCorrect = false, + selectedOption = null, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onOptionSelect, + onContinue, + className = '' +}: ROARPhonemeGameCoreProps) { + const optionsRef = useRef(null); + + const getLocalizedText = (key: 'correctMessage' | 'wrongMessage') => { + const messages = { + correctMessage: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿ!', + mr: '🎉 बरोबर!' + }, + wrongMessage: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + + return messages[key][selectedLanguage] || messages[key].en; + }; + + // Enhanced audio function for different languages - tries audio files first, then TTS + const playAudio = async (text: string, language: Language) => { + // Cancel any ongoing speech to prevent overlapping + speechSynthesis.cancel(); + + // Try to play audio file from sound-match folder first + const word = text.toLowerCase().trim(); + console.log(word) + const audioPath = `/audio/audio-preview/combined-word-games/sound-match/${language}/${word}.wav`; + + try { + const audio = new Audio(audioPath); + attachSlowLoadToast(audio); + + // Try to play the audio file + await new Promise((resolve, reject) => { + audio.onloadeddata = () => { + audio.play().then(() => { + audio.onended = () => resolve(); + }).catch(() => { + // If playback fails, fall through to TTS + reject(); + }); + }; + + audio.onerror = () => { + // If file doesn't exist or fails to load, fall through to TTS + reject(); + }; + + // Set a timeout to prevent hanging + setTimeout(() => { + if (!audio.ended && audio.readyState < 2) { + reject(); + } + }, 2000); + }); + + // Successfully played audio file + return; + } catch (error) { + // Fall back to TTS if audio file doesn't exist or fails + console.warn(`Audio file not found: ${audioPath}, falling back to TTS`); + } + + // Fallback to TTS with improved voice selection + setTimeout(() => { + const utterance = new SpeechSynthesisUtterance(text); + + // Natural speech settings for clear pronunciation + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + break; + case 'kn': + utterance.lang = 'kn-IN'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + break; + default: + utterance.lang = 'en-US'; + utterance.rate = 0.9; + utterance.pitch = 1.0; + utterance.volume = 0.9; + } + + // Simplified voice selection to prevent duplicate audio + const voices = speechSynthesis.getVoices(); + let selectedVoice = null; + + if (language === 'te') { + selectedVoice = + voices.find(voice => voice.lang === 'te-IN' || voice.lang === 'te') || + voices.find(voice => voice.lang === 'hi-IN' || voice.lang === 'hi') || + voices[0]; + } else if (language === 'mr') { + selectedVoice = + voices.find(voice => voice.lang === 'mr-IN' || voice.lang === 'mr') || + voices.find(voice => voice.lang === 'hi-IN' || voice.lang === 'hi') || + voices[0]; + } else { + selectedVoice = + voices.find(voice => voice.lang === 'en-US') || + voices.find(voice => voice.lang.startsWith('en')) || + voices[0]; + } + + if (selectedVoice) { + utterance.voice = selectedVoice; + } + + speechSynthesis.speak(utterance); + }, 50); + }; + + // Helper function to get target word for pronunciation + const getTargetWordText = (targetWord: string): string => { + return targetWord; // Just the word for clear pronunciation + }; + + return ( +
+
+
+
+
!disabled && playAudio( + getTargetWordText(currentQuestion.target.word), + selectedLanguage + )} + > + 🔊 +
+
+ + {/* Target Word Display */} +
+
+
{currentQuestion.target.image}
+
+
+
+ + {/* Visual Options */} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+ {currentQuestion.options.map((option, index) => ( + + ))} +
+
+ + {/* Feedback Area - Show in both game and preview modes */} + {showFeedback && ( +
+
+

+ {isCorrect ? getLocalizedText('correctMessage') : getLocalizedText('wrongMessage')} +

+
+ + {/* Continue Button - Only show in game mode */} + +
+ )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/ROARPhonemeGamePreview.tsx b/src/lib/axl-explorations/src/components/games/ROARPhonemeGamePreview.tsx new file mode 100644 index 00000000..6330111b --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARPhonemeGamePreview.tsx @@ -0,0 +1,702 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio, attachSlowLoadToast } from "../../utils/audioUtils"; +import { ROARPhonemeGameCore, type ROARPhonemeQuestion } from "./ROARPhonemeGameCore"; + +interface ROARPhonemeGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForSpeaker' // Wait for user to click speaker + | 'showTarget' // Show target image after speaker click + | 'instruction2' // After showing target, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Sound Match", + description: "Match sounds with pictures that start with the same letter!", + steps: [ + "🔊 Click the speaker to hear the sound", + "👀 Look at the target picture shown", + "🎯 Find the word that starts with the same letter", + "✨ Click the correct option!" + ], + instruction1: "Click the speaker icon to hear a word sound", + instruction2: "Good! Now look at this target picture carefully", + instruction3: "Now find which picture starts with the same letter sound", + instruction4: "Perfect! You matched the sounds correctly!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Click the speaker icon to hear a word sound", + narration2: "Good! Now look at this target picture carefully", + narration3: "Now find which picture starts with the same letter sound", + narration4: "Perfect! You matched the sounds correctly!", + howToPlay: "How to Play", + findMatchingSound: "Find the matching sound!", + demo: { + target: "🍞", + targetSound: "BREAD", + options: ["🍊", "🚲", "4️⃣", "🦵"], + correctIndex: 1, + explanation: "BREAD starts with 'B' and BICYCLE also starts with 'B'!" + } + }, + te: { + title: "ధ్వని మ్యాచ్", + description: "అదే అక్షరంతో ప్రారంభమయ్యే ధ్వనులు మరియు చిత్రాలను సరిపోల్చండి!", + steps: [ + "🔊 స్పీకర్‌ను క్లిక్ చేసి ధ్వనిని వినండి", + "👀 చూపించిన లక్ష్య చిత్రాన్ని చూడండి", + "🎯 అదే అక్షరంతో మొదలయ్యే పదాన్ని కనుగొనండి", + "✨ సరైన ఎంపికను క్లిక్ చేయండి!" + ], + instruction1: "పద ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ లక్ష్య చిత్రాన్ని జాగ్రత్తగా చూడండి", + instruction3: "ఇప్పుడు ఏ చిత్రం అదే అక్షర ధ్వనితో ప్రారంభమవుతుందో కనుగొనండి", + instruction4: "పర్ఫెక్ట్! మీరు ధ్వనులను సరిగ్గా సరిపోల్చారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "పద ధ్వనిని వినడానికి స్పీకర్ చిహ్నంపై క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ లక్ష్య చిత్రాన్ని జాగ్రత్తగా చూడండి", + narration3: "ఇప్పుడు ఏ చిత్రం అదే అక్షర ధ్వనితో ప్రారంభమవుతుందో కనుగొనండి", + narration4: "పర్ఫెక్ట్! మీరు ధ్వనులను సరిగ్గా సరిపోల్చారు!", + howToPlay: "ఎలా ఆడాలి", + findMatchingSound: "సరిపోయే ధ్వనిని కనుగొనండి!", + demo: { + target: "🐦", + targetSound: "పక్షి", + options: ["📚", "🚀", "👕", "🎈"], + correctIndex: 0, + explanation: "పక్షి 'ప' అక్షరంతో మొదలవుతుంది మరియు పుస్తకం కూడా 'ప' అక్షరంతో మొదలవుతుంది!" + } + }, + kn: { + title: "ಶಬ್ದ ಹೊಂದಾಣಿಕೆ", + description: "ಅದೇ ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾಗುವ ಶಬ್ದಗಳು ಮತ್ತು ಚಿತ್ರಗಳನ್ನು ಹೊಂದಿಸಿ!", + steps: [ + "🔊 ಸ್ಪೀಕರ್ ಅನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ ಶಬ್ದವನ್ನು ಕೇಳಿ", + "👀 ತೋರಿಸಲಾದ ಗುರಿ ಚಿತ್ರವನ್ನು ನೋಡಿ", + "🎯 ಅದೇ ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾಗುವ ಪದವನ್ನು ಹುಡುಕಿ", + "✨ ಸರಿಯಾದ ಆಯ್ಕೆಯನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ!" + ], + instruction1: "ಪದ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗುರಿ ಚಿತ್ರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + instruction3: "ಈಗ ಯಾವ ಚಿತ್ರ ಅದೇ ಅಕ್ಷರ ಶಬ್ದದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ ಎಂದು ಹುಡುಕಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಶಬ್ದಗಳನ್ನು ಸರಿಯಾಗಿ ಹೊಂದಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಪದ ಶಬ್ದವನ್ನು ಕೇಳಲು ಸ್ಪೀಕರ್ ಐಕಾನ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗುರಿ ಚಿತ್ರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + narration3: "ಈಗ ಯಾವ ಚಿತ್ರ ಅದೇ ಅಕ್ಷರ ಶಬ್ದದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ ಎಂದು ಹುಡುಕಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಶಬ್ದಗಳನ್ನು ಸರಿಯಾಗಿ ಹೊಂದಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + findMatchingSound: "ಹೊಂದಾಣಿಕೆಯ ಶಬ್ದವನ್ನು ಹುಡುಕಿ!", + demo: { + target: "🐦", + targetSound: "ಪಕ್ಷಿ", + options: ["📚", "🚀", "👕", "🎈"], + correctIndex: 0, + explanation: "ಪಕ್ಷಿ 'ಪ' ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ ಮತ್ತು ಪುಸ್ತಕ ಕೂಡ 'ಪ' ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ!" + } + }, + mr: { + title: "आवाज जुळवा", + description: "तोच अक्षराने सुरू होणारे आवाज आणि चित्रे जुळवा!", + steps: [ + "🔊 स्पीकरवर क्लिक करून आवाज ऐका", + "👀 दाखवलेले लक्ष्य चित्र पहा", + "🎯 तोच अक्षराने सुरू होणारे शब्द शोधा", + "✨ योग्य पर्यायावर क्लिक करा!" + ], + instruction1: "शब्द आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + instruction2: "चांगले! आता हे लक्ष्य चित्र काळजीपूर्वक पहा", + instruction3: "आता कोणते चित्र तोच अक्षर आवाजाने सुरू होते ते शोधा", + instruction4: "उत्कृष्ट! तुम्ही आवाज योग्यरित्या जुळवले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "शब्द आवाज ऐकण्यासाठी स्पीकर चिन्हावर क्लिक करा", + narration2: "चांगले! आता हे लक्ष्य चित्र काळजीपूर्वक पहा", + narration3: "आता कोणते चित्र तोच अक्षर आवाजाने सुरू होते ते शोधा", + narration4: "उत्कृष्ट! तुम्ही आवाज योग्यरित्या जुळवले!", + howToPlay: "कसे खेळायचे", + findMatchingSound: "जुळणारा आवाज शोधा!", + demo: { + target: "🍎", + targetSound: "सफरचंद", + options: ["☀️", "🚀", "👕", "📚"], + correctIndex: 0, + explanation: "सफरचंद 'स' अक्षराने सुरू होतो आणि सूर्य देखील 'स' अक्षराने सुरू होतो!" + } + } +}; + +export function ROARPhonemeGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: ROARPhonemeGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedSpeaker, setHasClickedSpeaker] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showTarget, setShowTarget] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const speakerButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question object for ROARPhonemeGameCore + const getDemoOptions = () => { + const lang = contentLanguage; + if (lang === 'te') { + // Telugu: పక్షి (bird) matches with 📚 (పుస్తకం - book) + return [ + { image: "📚", word: "పుస్తకం", phoneme: "ప" }, // Book - correct match + { image: "🚀", word: "రాకెట్", phoneme: "ర" }, // Rocket + { image: "👕", word: "చొక్కా", phoneme: "చ" }, // Shirt + { image: "🎈", word: "బలూన్", phoneme: "బ" } // Balloon + ]; + } else if (lang === 'kn') { + // Kannada: ಪಕ్షಿ (bird) matches with 📚 (ಪುಸ್ತಕ - book) + return [ + { image: "📚", word: "ಪುಸ್ತಕ", phoneme: "ಪ" }, // Book - correct match + { image: "🚀", word: "ರಾಕೆಟ್", phoneme: "ರ" }, // Rocket + { image: "👕", word: "ಅಂಗಿ", phoneme: "ಅ" }, // Shirt + { image: "🎈", word: "ಬಲೂನ್", phoneme: "ಬ" } // Balloon + ]; + } else if (lang === 'mr') { + // Marathi: सफरचंद (apple) matches with ☀️ (सूर्य - sun) + return [ + { image: "☀️", word: "सूर्य", phoneme: "स" }, // Sun - correct match (both start with स) + { image: "🚀", word: "रॉकेट", phoneme: "र" }, // Rocket + { image: "👕", word: "शर्ट", phoneme: "श" }, // Shirt + { image: "📚", word: "पुस्तक", phoneme: "प" } // Book + ]; + } else { + // English: BREAD matches with BICYCLE + return [ + { image: "🍊", word: "ORANGE", phoneme: "O" }, // Orange + { image: "🚲", word: "BICYCLE", phoneme: "B" }, // Bicycle - correct match + { image: "4️⃣", word: "FOUR", phoneme: "F" }, // Four + { image: "🦵", word: "LEG", phoneme: "L" } // Leg + ]; + } + }; + + const demoQuestion: ROARPhonemeQuestion = { + target: { + image: instructions.demo.target, + word: instructions.demo.targetSound, + phoneme: instructions.demo.targetSound.charAt(0).toUpperCase() // First letter as phoneme + }, + options: getDemoOptions(), + audio: instructions.demo.targetSound, + complexity: 'easy' + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined word games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined word games audio files for Sound Match + const gameName = 'Combined Word Games'; + const subGame = 'Sound Match'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Play word sound - tries audio files first, then TTS + const playWordSound = async (text: string) => { + const language = contentLanguage; + + // Try to play audio file from sound-match folder first + const word = text.toLowerCase().trim(); + const audioPath = `/audio/audio-preview/combined-word-games/sound-match/${language}/${word}.wav`; + + try { + const audio = new Audio(audioPath); + attachSlowLoadToast(audio); + + // Try to play the audio file + await new Promise((resolve, reject) => { + audio.onloadeddata = () => { + audio.play().then(() => { + audio.onended = () => resolve(); + }).catch(() => { + // If playback fails, fall through to TTS + reject(); + }); + }; + + audio.onerror = () => { + // If file doesn't exist or fails to load, fall through to TTS + reject(); + }; + + // Set a timeout to prevent hanging + setTimeout(() => { + if (!audio.ended && audio.readyState < 2) { + reject(); + } + }, 2000); + }); + + // Successfully played audio file + return; + } catch (error) { + // Fall back to TTS if audio file doesn't exist or fails + console.warn(`Audio file not found: ${audioPath}, falling back to TTS`); + } + + // Fallback to TTS + const utterance = new SpeechSynthesisUtterance(text); + const langForTTS = language as Language; + utterance.lang = langForTTS === 'te' ? 'te-IN' : + langForTTS === 'kn' ? 'kn-IN' : + langForTTS === 'mr' ? 'mr-IN' : + langForTTS === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForSpeaker': + setCurrentStep(0); + break; + case 'showTarget': + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedSpeaker(false); + setSelectedAnswer(null); + setShowFeedback(false); + setShowTarget(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForSpeaker + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForSpeaker'); + setTimeout(() => { + speakerButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle speaker button click + const handleSpeakerClick = async () => { + if (demoStep !== 'waitForSpeaker' || hasClickedSpeaker) return; + + setHasClickedSpeaker(true); + playWordSound(instructions.demo.targetSound); + + // Show target image + setShowTarget(true); + setDemoStep('showTarget'); + + // Wait a moment then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle option click + const handleOptionClick = async (index: number) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + setSelectedAnswer(index); + setShowFeedback(true); + + const isCorrect = index === instructions.demo.correctIndex; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedSpeaker(false); + setShowTarget(false); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showSpeaker = demoStep === 'waitForSpeaker' || demoStep === 'showTarget' || demoStep === 'instruction1' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + const showOptions = demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + setTimeout(() => { + onStartGame(); + }, 100); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + setTimeout(() => { + onBack(); + }, 100); + }; + + // Start game handler + const handleStartGame = () => { + stopAllAudio(); + setTimeout(() => { + onStartGame(); + }, 100); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Fixed Layout Structure */} +
+ {/* Speaker Button Section - Always visible but disabled initially */} + {(demoStep === 'instruction1' || demoStep === 'waitForSpeaker') && ( +
+
+
+ 🔊 +
+
+ {demoStep === 'waitForSpeaker' && ( +
+ 👆 +
+ )} +
+ )} + + {/* Target Display - Show after speaker click */} + {(demoStep === 'showTarget' || demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions && ( +
+
+
{instructions.demo.target}
+
+
+ )} + + {/* Options Grid - Show after instruction 3 */} + {showOptions && ( + { + const index = demoQuestion.options.findIndex(opt => opt.word === optionWord); + if (index !== -1) { + handleOptionClick(index); + } + }} + /> + )} + +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} + +export default ROARPhonemeGamePreview; diff --git a/src/lib/axl-explorations/src/components/games/ROARPictureVocabGame.tsx b/src/lib/axl-explorations/src/components/games/ROARPictureVocabGame.tsx new file mode 100644 index 00000000..c029022f --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARPictureVocabGame.tsx @@ -0,0 +1,860 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Globe } from "lucide-react"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import ROARPictureVocabGamePreview from "./ROARPictureVocabGamePreview"; +import { pictureWordsDataLoader } from "../../utils/pictureWordsDataLoader"; +import { ROARPictureVocabGameCore, type ROARPictureVocabQuestion } from "./ROARPictureVocabGameCore"; + +// ROARPictureVocabQuestion interface is now imported from ROARPictureVocabGameCore + +interface ROARPictureVocabGameProps { + onBack: () => void; +} + +export function ROARPictureVocabGame({ onBack }: ROARPictureVocabGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedOption, setSelectedOption] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + + // ✅ CHILD-FRIENDLY: Track used questions to prevent repetition + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations - All languages have 10 levels + const getLanguageLevels = (language: Language) => { + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `pictureWords_${selectedLanguage}` : 'pictureWords'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel !== null ? selectedLevel : gameProgress.currentLevel || 1; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = selectedLanguage ? getLanguageLevels(selectedLanguage) : { maxLevels: 10 }; + + // Debug logging for Marathi level display + if (selectedLanguage === 'mr') { + console.log('🔍 Marathi Level Debug:', { + selectedLevel, + gameProgressCurrentLevel: gameProgress.currentLevel, + calculatedCurrentLevel: currentLevel, + gameKey, + languageLevels + }); + } + + // Initialize game session and questions when language/level selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel && !isGameComplete) { + try { + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + // ✅ IMPROVED: Generate unique questions with level-specific seed + const uniqueSeed = `${selectedLanguage}_${currentLevel}_${Date.now()}`; + const newQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() // Always start with fresh questions + ); + + // ✅ ADDED: Validate questions before setting + if (newQuestions && newQuestions.length > 0) { + setQuestions(newQuestions); + // ✅ FIXED: Reset used questions for new session + setUsedQuestions(new Set()); + console.log(`🎮 Starting ${selectedLanguage} game at level ${currentLevel} with complexity: ${difficultySettings.complexity}`); + console.log(`🎯 Generated ${newQuestions.length} unique questions for session: ${uniqueSeed}`); + } else { + console.error(`❌ Failed to generate questions for ${selectedLanguage} level ${currentLevel}`); + // Fallback: generate basic questions + const fallbackQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + 1, // Fallback to level 1 + 'basic', + 10, + new Set() + ); + setQuestions(fallbackQuestions); + } + } catch (error) { + console.error('❌ Error initializing game session:', error); + // Fallback to basic questions + const fallbackQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + 1, + 'basic', + 10, + new Set() + ); + setQuestions(fallbackQuestions); + } + } else if (selectedLanguage && !selectedLevel) { + // ✅ ADDED: Clear questions when language is selected but no level yet + setQuestions([]); + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsCorrect(false); + setShowLevelUp(false); + setIsGameComplete(false); + setUsedQuestions(new Set()); + console.log(`🎮 Language ${selectedLanguage} selected, waiting for level selection`); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute backendCurrentLevel from progress (score > 0 or completed advances next) + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + const computedNextLevel = Math.min(Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), languageLevels.maxLevels); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min(Math.max(computedNextLevel, backendProvided), languageLevels.maxLevels); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setShowPreview(false); // Hide preview when level is selected + setUsedQuestions(new Set()); + } + }, [selectedLevel, selectedLanguage]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + const handleOptionSelect = async (optionWord: string) => { + if (showFeedback) return; + + setSelectedOption(optionWord); + const correct = optionWord === currentQuestion.target.word; + setIsCorrect(correct); + setShowFeedback(true); + + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `picturewords_${currentLevel}_${currentQuestionIndex}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'pictureWords', + optionWord, + currentQuestion.target.word, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'pictureWords', + userAnswer: optionWord, + correctAnswer: currentQuestion.target.word, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 1); + setTotalCorrect(prevTotal => prevTotal + 1); + } + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedOption(null); + setShowFeedback(false); + } else { + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Picture Words Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const handleBackClick = async () => { + await sessionTelemetryManager.endSubSessionWithBackButton(); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsCorrect(false); + setShowLevelUp(false); + setLevelFailed(false); + setIsGameComplete(false); // ✅ FIXED: Reset game completion state + + // ✅ FIXED: Reset session cache for completely fresh questions + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + // ✅ IMPROVED: Handle different reset scenarios + if (selectedLanguage && selectedLevel) { + // Scenario 1: Reset current level with fresh questions + try { + const session = startSession(gameKey); + // ✅ IMPROVED: Generate fresh questions with timestamp for uniqueness + const resetSeed = `${selectedLanguage}_${currentLevel}_reset_${Date.now()}`; + const newQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() // Fresh start with no used questions + ); + + // ✅ ADDED: Validate questions before setting + if (newQuestions && newQuestions.length > 0) { + setQuestions(newQuestions); + console.log(`🔄 Reset game with ${newQuestions.length} fresh questions for session: ${resetSeed}`); + } else { + console.error(`❌ Failed to generate questions for reset game`); + // Fallback to basic questions + const fallbackQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + 1, + 'basic', + 10, + new Set() + ); + setQuestions(fallbackQuestions); + } + } catch (error) { + console.error('❌ Error resetting game:', error); + // Fallback to basic questions + const fallbackQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + 1, + 'basic', + 10, + new Set() + ); + setQuestions(fallbackQuestions); + } + } else if (selectedLanguage && !selectedLevel) { + // Scenario 2: All levels completed, restart from beginning + console.log('🔄 All levels completed, restarting from beginning'); + navigate('/roar-picture-vocab-game/level/1'); // Navigate to level 1 + + // Generate questions for level 1 + try { + const session = startSession(gameKey); + const newQuestions = generateROARPictureVocabQuestions( + selectedLanguage, + 1, // Start from level 1 + 'basic', // Start with basic complexity + 10, + new Set() + ); + + if (newQuestions && newQuestions.length > 0) { + setQuestions(newQuestions); + console.log(`🔄 Restarted game from level 1 with ${newQuestions.length} questions`); + } else { + console.error('❌ Failed to generate questions for restart'); + setQuestions([]); + } + } catch (error) { + console.error('❌ Error restarting game:', error); + setQuestions([]); + } + } else { + // Scenario 3: No language selected, clear everything + setQuestions([]); + console.log('🔄 Reset game: No language selected, clearing questions'); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/roar-picture-vocab-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/roar-picture-vocab-game'); + }; + + // ✅ ADDED: Missing function to map complexity to vocabulary set + const mapComplexityToVocabSet = (complexity: string): string => { + switch (complexity.toLowerCase()) { + case 'basic': + return 'basic'; + case 'intermediate': + return 'intermediate'; + case 'advanced': + return 'advanced'; + case 'expert': + return 'expert'; + case 'master': + return 'master'; + default: + return 'basic'; + } + }; + + const calculateStars = () => { + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (totalCorrect === questions.length) { + achievements.push("Vocabulary Master - Perfect Word Recognition!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Word Detective - Great Vocabulary Skills!"); + } + if (showLevelUp) { + achievements.push(`Level Up! Now at Level ${gameProgress.currentLevel}`); + } + return achievements; + }; + + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level selector is requested (after preview) + if (showLevelSelector) { + const languageLevels = getLanguageLevels(selectedLanguage); + const levels = Array.from({ length: languageLevels.maxLevels }, (_, i) => i + 1); + + // Ensure current level is properly bounded for Marathi + const levelSelectorCurrentLevel = Math.max(1, Math.min(gameProgress.currentLevel || 1, languageLevels.maxLevels)); + + return ( + { + setShowPreview(true); + onBack(); + }} // Go back to preview instead of main menu + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Picture Words" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + { + // ✅ FIXED: Proper level advancement using manuallyAdvanceLevel + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage} (max: ${languageLevels.maxLevels})`); + + // Manually advance the level using the learning progress hook + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL + navigate(`/roar-picture-vocab-game/level/${nextLevel}`); + + // Reset game state for new level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedOption(null); + setShowFeedback(false); + setIsCorrect(false); + setShowLevelUp(false); + setLevelFailed(false); + setIsGameComplete(false); + + // ✅ FIXED: Reset used questions for new level + setUsedQuestions(new Set()); + + // ✅ ADDED: If all levels completed, show completion message + if (nextLevel >= languageLevels.maxLevels) { + console.log('🎉 All levels completed for this language!'); + } + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion && selectedLanguage && selectedLevel) { + return ( +
+
Loading...
+
+ ); + } + + // Show level selector if language is selected but no level yet + if (selectedLanguage && !selectedLevel && !showLevelSelector) { + return ( +
+
+
Please select a level to start playing
+ +
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Picture Words +

+
+ + + Level {currentLevel} • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ +
+
+
+
+ ); +} + +// Generate ROAR Picture Vocab questions with language-specific content +function generateROARPictureVocabQuestions( + language: Language, + level: number, + complexity: string, + count: number, + usedQuestions: Set +): ROARPictureVocabQuestion[] { + // ✅ ADDED: Input validation + if (!language || !complexity || count <= 0) { + console.error('❌ Invalid input parameters for question generation'); + return []; + } + + // ✅ ADDED: Validate level range + const maxLevels = language === 'te' ? 15 : language === 'mr' ? 12 : language === 'kn' ? 10 : 10; + if (level < 1 || level > maxLevels) { + console.error(`❌ Invalid level ${level} for language ${language}`); + return []; + } + + // Map level to complexity level + const mapLevelToComplexity = (level: number): 'basic' | 'intermediate' | 'advanced' | 'expert' | 'master' => { + if (level <= 2) return 'basic'; + if (level <= 4) return 'intermediate'; + if (level <= 6) return 'advanced'; + if (level <= 8) return 'expert'; + return 'master'; + }; + + const complexityLevel = mapLevelToComplexity(level); + + // Use JSON data loader instead of hardcoded content + // Ensure only supported languages are passed (pictureWordsDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const vocabData = pictureWordsDataLoader.getPictureWords(supportedLanguage, complexityLevel); + + if (!vocabData || vocabData.length === 0) { + console.warn(`⚠️ No vocabulary data found for language: ${language}, complexity: ${complexityLevel}`); + return []; + } + + // Filter out already used questions + const availableVocab = vocabData.filter(item => !usedQuestions.has(item.word)); + + if (availableVocab.length === 0) { + console.warn(`⚠️ All vocabulary items have been used for ${language} at ${complexityLevel} level`); + return []; + } + + // Shuffle and limit vocabulary + const shuffledVocab = availableVocab.sort(() => Math.random() - 0.5); + const selectedVocab = shuffledVocab.slice(0, Math.min(count, shuffledVocab.length)); + + // Generate questions with options + const questions: ROARPictureVocabQuestion[] = []; + + for (let i = 0; i < selectedVocab.length; i++) { + const target = selectedVocab[i]; + + // Generate 3 distractors from the same vocabulary set + const distractors = availableVocab + .filter(item => item.word !== target.word && item.category !== target.category) + .sort(() => Math.random() - 0.5) + .slice(0, 3); + + // If not enough distractors from different categories, add from same category + const finalDistractors = distractors.length >= 3 ? distractors : + availableVocab.filter(item => item.word !== target.word); + + const shuffledDistractors = finalDistractors.sort(() => Math.random() - 0.5); + + const options = [target]; + for (let j = 0; j < 3 && j < shuffledDistractors.length; j++) { + options.push(shuffledDistractors[j]); + } + + // Shuffle options + const shuffledOptions = options.sort(() => Math.random() - 0.5); + + questions.push({ + target: { + image: target.image, + word: target.word, + category: target.category + }, + options: shuffledOptions.map(opt => ({ + image: opt.image, + word: opt.word, + category: opt.category + })), + audio: target.word, + complexity, + language + }); + } + + // ✅ ADDED: Validate final question set + if (questions.length < count) { + console.warn(`⚠️ Only generated ${questions.length}/${count} questions due to vocabulary constraints`); + } + + return questions; +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/ROARPictureVocabGameCore.tsx b/src/lib/axl-explorations/src/components/games/ROARPictureVocabGameCore.tsx new file mode 100644 index 00000000..bc8378f7 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARPictureVocabGameCore.tsx @@ -0,0 +1,145 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { Language } from "../../constants/languages"; +import { ContinueButton } from "./ContinueButton"; + +export interface ROARPictureVocabQuestion { + target: { + image: string; + word: string; + category: string; + }; + options: Array<{ + image: string; + word: string; + category: string; + }>; + audio: string; + complexity: string; + language: Language; +} + +export interface ROARPictureVocabGameCoreProps { + currentQuestion: ROARPictureVocabQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + showFeedback?: boolean; + isCorrect?: boolean; + selectedOption?: string | null; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onOptionSelect: (optionWord: string) => void; + onContinue?: () => void; // Continue button callback + feedbackLanguageOverride?: Language; + className?: string; +} + +export function ROARPictureVocabGameCore({ + currentQuestion, + mode, + selectedLanguage, + showFeedback = false, + isCorrect = false, + selectedOption = null, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onOptionSelect, + onContinue, + feedbackLanguageOverride, + className = '' +}: ROARPictureVocabGameCoreProps) { + const optionsRef = useRef(null); + + const getLocalizedText = (key: string) => { + const texts = { + successMessage: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿ!', + mr: '🎉 बरोबर!' + }, + failureMessage: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + const language = selectedLanguage || 'en'; + return texts[key as keyof typeof texts]?.[language] || texts[key as keyof typeof texts]?.en || ''; + }; + + return ( +
+
+
+ {/* Target Word Display */} +
+
+ {currentQuestion.target.word} +
+
+
+ + {/* Visual Options */} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+ {currentQuestion.options.map((option, index) => ( + + ))} +
+
+ + {/* Feedback Area - Show in both game and preview modes */} + {showFeedback && ( +
+
+

+ {isCorrect ? getLocalizedText('successMessage') : getLocalizedText('failureMessage')} +

+
+ + {/* Continue Button - Only show in game mode */} + +
+ )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/ROARPictureVocabGamePreview.tsx b/src/lib/axl-explorations/src/components/games/ROARPictureVocabGamePreview.tsx new file mode 100644 index 00000000..89cd8c9e --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARPictureVocabGamePreview.tsx @@ -0,0 +1,630 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { ROARPictureVocabGameCore, type ROARPictureVocabQuestion } from "./ROARPictureVocabGameCore"; + +interface ROARPictureVocabGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" + | 'showWord' // Show the word after ready click + | 'instruction2' // After showing word, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show picture options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Picture Words", + description: "Match words with the correct pictures!", + steps: [ + "📖 Read the word carefully", + "👀 Look at all the picture options", + "🎯 Find the picture that matches the word", + "✨ Click the correct picture!" + ], + instruction1: "Click 'I'm Ready' to see the word", + instruction2: "Good! Now read this word carefully", + instruction3: "Now find which picture matches this word", + instruction4: "Perfect! You matched the word with the correct picture!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Click I'm Ready to see the word", + narration2: "Good! Now read this word carefully", + narration3: "Now find which picture matches this word", + narration4: "Perfect! You matched the word with the correct picture!", + howToPlay: "How to Play", + findMatchingPicture: "Find the matching picture!", + demo: { + word: "CAT", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "The word 'CAT' matches the cat picture!" + } + }, + te: { + title: "చిత్ర పదాలు", + description: "పదాలను సరైన చిత్రాలతో సరిపోల్చండి!", + steps: [ + "📖 పదాన్ని జాగ్రత్తగా చదవండి", + "👀 అన్ని చిత్ర ఎంపికలను చూడండి", + "🎯 పదానికి సరిపోయే చిత్రాన్ని కనుగొనండి", + "✨ సరైన చిత్రాన్ని క్లిక్ చేయండి!" + ], + instruction1: "పదాన్ని చూడటానికి 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + instruction3: "ఇప్పుడు ఈ పదానికి సరిపోయే చిత్రాన్ని కనుగొనండి", + instruction4: "పర్ఫెక్ట్! మీరు పదాన్ని సరైన చిత్రంతో సరిపోల్చారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "పదాన్ని చూడటానికి నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + narration3: "ఇప్పుడు ఈ పదానికి సరిపోయే చిత్రాన్ని కనుగొనండి", + narration4: "పర్ఫెక్ట్! మీరు పదాన్ని సరైన చిత్రంతో సరిపోల్చారు!", + howToPlay: "ఎలా ఆడాలి", + findMatchingPicture: "సరిపోయే చిత్రాన్ని కనుగొనండి!", + demo: { + word: "పిల్లి", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "పదం 'పిల్లి' పిల్లి చిత్రంతో సరిపోతుంది!" + } + }, + kn: { + title: "ಚಿತ್ರ ಪದಗಳು", + description: "ಪದಗಳನ್ನು ಸರಿಯಾದ ಚಿತ್ರಗಳೊಂದಿಗೆ ಹೊಂದಿಸಿ!", + steps: [ + "📖 ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "👀 ಎಲ್ಲಾ ಚಿತ್ರ ಆಯ್ಕೆಗಳನ್ನು ನೋಡಿ", + "🎯 ಪದಕ್ಕೆ ಹೊಂದಾಣಿಕೆಯ ಚಿತ್ರವನ್ನು ಹುಡುಕಿ", + "✨ ಸರಿಯಾದ ಚಿತ್ರವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ!" + ], + instruction1: "ಪದವನ್ನು ನೋಡಲು 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ಈಗ ಈ ಪದಕ್ಕೆ ಹೊಂದಾಣಿಕೆಯ ಚಿತ್ರವನ್ನು ಹುಡುಕಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾದ ಚಿತ್ರದೊಂದಿಗೆ ಹೊಂದಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಪದವನ್ನು ನೋಡಲು ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ಈಗ ಈ ಪದಕ್ಕೆ ಹೊಂದಾಣಿಕೆಯ ಚಿತ್ರವನ್ನು ಹುಡುಕಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾದ ಚಿತ್ರದೊಂದಿಗೆ ಹೊಂದಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + findMatchingPicture: "ಹೊಂದಾಣಿಕೆಯ ಚಿತ್ರವನ್ನು ಹುಡುಕಿ!", + demo: { + word: "ಬೆಕ್ಕು", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "ಪದ 'ಬೆಕ್ಕು' ಬೆಕ್ಕಿನ ಚಿತ್ರಕ್ಕೆ ಹೊಂದಾಣಿಕೆಯಾಗುತ್ತದೆ!" + } + }, + mr: { + title: "चित्र शब्द", + description: "शब्दांना योग्य चित्रांशी जुळवा!", + steps: [ + "📖 शब्द काळजीपूर्वक वाचा", + "👀 सर्व चित्र पर्याय पहा", + "🎯 शब्दाशी जुळणारे चित्र शोधा", + "✨ योग्य चित्रावर क्लिक करा!" + ], + instruction1: "शब्द पाहण्यासाठी 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + instruction3: "आता या शब्दाशी जुळणारे चित्र शोधा", + instruction4: "उत्कृष्ट! तुम्ही शब्दाला योग्य चित्राशी जुळवले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "शब्द पाहण्यासाठी मी तयार आहे क्लिक करा", + narration2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + narration3: "आता या शब्दाशी जुळणारे चित्र शोधा", + narration4: "उत्कृष्ट! तुम्ही शब्दाला योग्य चित्राशी जुळवले!", + howToPlay: "कसे खेळायचे", + findMatchingPicture: "जुळणारे चित्र शोधा!", + demo: { + word: "मांजर", + options: ["🐱", "🐶", "🐭", "🐰"], + correctIndex: 0, + explanation: "शब्द 'मांजर' मांजराच्या चित्राशी जुळतो!" + } + } +}; + +export function ROARPictureVocabGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: ROARPictureVocabGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showWord, setShowWord] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question object for ROARPictureVocabGameCore + const demoQuestion: ROARPictureVocabQuestion = { + target: { + image: instructions.demo.options[instructions.demo.correctIndex], + word: instructions.demo.word, + category: "demo" + }, + options: instructions.demo.options.map((option, index) => ({ + image: option, + word: index === instructions.demo.correctIndex ? instructions.demo.word : `option${index}`, + category: "demo" + })), + audio: instructions.demo.word, + complexity: "basic", + language: contentLanguage + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined word games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined word games audio files for Picture Words + const gameName = 'Combined Word Games'; + const subGame = 'Picture Words'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Play word sound + const playWordSound = (text: string) => { + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = audioLanguage === 'te' ? 'te-IN' : + audioLanguage === 'kn' ? 'kn-IN' : + audioLanguage === 'mr' ? 'mr-IN' : + audioLanguage === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + speechSynthesis.speak(utterance); + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + setCurrentStep(0); + break; + case 'showWord': + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setSelectedAnswer(null); + setShowFeedback(false); + setShowWord(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle ready button click + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + playWordSound(instructions.demo.word); + + // Show word + setShowWord(true); + setDemoStep('showWord'); + + // Wait a moment then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle option click + const handleOptionClick = async (index: number) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + setSelectedAnswer(index); + setShowFeedback(true); + + const isCorrect = index === instructions.demo.correctIndex; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowWord(false); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showWordDisplay = showWord && (demoStep === 'showWord' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'); + const showOptions = demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler with audio cleanup + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Start game handler with audio cleanup + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Fixed Layout Structure */} +
+ {/* Ready Button Section - Show initially */} + {(demoStep === 'instruction1' || demoStep === 'waitForReady') && !showWordDisplay && ( +
+
+ {contentLanguage === 'en' ? "I'm Ready" : contentLanguage === 'te' ? 'నేను సిద్ధంగా ఉన్నాను' : contentLanguage === 'kn' ? 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' : 'मी तयार आहे'} +
+ {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {/* Word Display - Show after ready click */} + {(demoStep === 'showWord' || demoStep === 'instruction2' || demoStep === 'instruction3') && !showOptions && ( +
+
+ {instructions.demo.word} +
+
+ )} + + {/* Options Grid - Show after instruction 3 */} + {showOptions && ( + { + const index = demoQuestion.options.findIndex(opt => opt.word === optionWord); + if (index !== -1) { + handleOptionClick(index); + } + }} + feedbackLanguageOverride={audioLanguage} + /> + )} + +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right with Pointer */} +
+ {/* Hand Pointer - appears when button is enabled */} + {completionCount >= 3 && ( +
+ 👉 +
+ )} + + +
+
+
+
+
+ + ); +} + +export default ROARPictureVocabGamePreview; \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/ROARRapidVisualGame.tsx b/src/lib/axl-explorations/src/components/games/ROARRapidVisualGame.tsx new file mode 100644 index 00000000..c8e58790 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARRapidVisualGame.tsx @@ -0,0 +1,1009 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ProgressBar } from "../ProgressBar"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import ROARRapidVisualGamePreview from "./ROARRapidVisualGamePreview"; +import { ROARRapidVisualGameCore, type ROARRapidVisualQuestion } from "./ROARRapidVisualGameCore"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Timer, Globe, Sparkles, EyeOff } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { memoryGameDataLoader } from "../../utils/memoryGameDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; + +// ROARRapidVisualQuestion interface is now imported from ROARRapidVisualGameCore + +interface ROARRapidVisualGameProps { + onBack: () => void; +} + +export function ROARRapidVisualGame({ onBack }: ROARRapidVisualGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedPosition, setSelectedPosition] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [timeRemaining, setTimeRemaining] = useState(0); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [showTargetLetter, setShowTargetLetter] = useState(true); + const [showSelectionGrid, setShowSelectionGrid] = useState(false); + const [levelFailed, setLevelFailed] = useState(false); + const [showTimeoutMessage, setShowTimeoutMessage] = useState(false); + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // ✅ CHILD-FRIENDLY: Track used questions to prevent repetition + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + + // Language-specific level configurations - All languages have 10 levels + const getLanguageLevels = (language: Language) => { + return { maxLevels: 10 }; + }; + + const languageLevels = selectedLanguage ? getLanguageLevels(selectedLanguage) : { maxLevels: 10 }; + const gameKey = selectedLanguage ? `quickSight_${selectedLanguage}` : 'quickSight'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel !== null ? selectedLevel : gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + + // Initialize game session and questions when language/level selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + // Add a small delay to ensure state reset completes first + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + const newQuestions = generateROARRapidVisualQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) - for individual games only + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Extract metadata (currentLevel from backend) + if (result.metadata?.currentLevel) { + setBackendCurrentLevel(result.metadata.currentLevel); + } + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedPosition(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setShowTimeoutMessage(false); + setShowPreview(false); // Hide preview when level is selected + setUsedQuestions(new Set()); + } + }, [selectedLevel, selectedLanguage]); + + const currentQuestion = questions[currentQuestionIndex]; + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + const getTimeLimit = (complexity: string) => { + // Base time limits by complexity + const baseTime = { + basic: 8, + intermediate: 6, + advanced: 5, + expert: 4, + master: 3 + }; + + // Additional level-based time reduction for higher levels + const levelBonus = Math.max(0, Math.floor((currentLevel - 1) * 0.2)); + + return Math.max(2, baseTime[complexity as keyof typeof baseTime] - levelBonus); + }; + + const handleTimeUp = useCallback(() => { + setIsTimerRunning(false); + setSelectedPosition(-1); // -1 indicates time up + setIsCorrect(false); + setShowFeedback(false); // Don't show feedback immediately + setShowTimeoutMessage(true); // Show timeout message + recordAnswer(false); + + // Hide target letter and show selection grid + setShowTargetLetter(false); + setShowSelectionGrid(true); + + // Don't auto-advance - player must manually click to continue + // This ensures they can't skip questions by waiting + }, []); + + const handlePositionSelect = useCallback(async (position: number) => { + if (showFeedback || !currentQuestion) return; + + setIsTimerRunning(false); + setSelectedPosition(position); + const correct = position === currentQuestion.targetPosition; + setIsCorrect(correct); + setShowFeedback(true); + setShowTimeoutMessage(false); + + // Record the answer for adaptive learning + recordAnswer(correct); + // Telemetry assess - send actual letters instead of position indices + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `quicksight_${currentLevel}_${currentQuestionIndex}`; + const userAnswerLetter = currentQuestion.letters[position] || ''; + const correctAnswerLetter = currentQuestion.letters[currentQuestion.targetPosition] || currentQuestion.target; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'quickSight', + userAnswerLetter, + correctAnswerLetter, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'quickSight', + userAnswer: userAnswerLetter, + correctAnswer: correctAnswerLetter, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 1); + setTotalCorrect(prevTotal => prevTotal + 1); + } + + // Don't auto-advance - player must manually continue + // This ensures they see feedback and can't skip questions + }, [showFeedback, currentQuestion, timeRemaining]); + + // Timer effect for rapid visual processing + useEffect(() => { + if (currentQuestion && !showFeedback && questions.length > 0) { + // Always reset timer state for new question + const timeLimit = getTimeLimit(difficultySettings.complexity); + setTimeRemaining(timeLimit); + setIsTimerRunning(true); + setShowTargetLetter(true); + setShowSelectionGrid(false); + setShowTimeoutMessage(false); // Reset timeout message for new question + + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + // Time's up - auto-submit wrong answer + handleTimeUp(); + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => { + clearInterval(timer); + setIsTimerRunning(false); + }; + } + }, [currentQuestionIndex, currentQuestion, showFeedback, difficultySettings.complexity, questions.length]); + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/roar-rapid-visual-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/roar-rapid-visual-game'); + }; + + const getAvailableLevels = () => { + return Array.from({ length: languageLevels.maxLevels }, (_, i) => i + 1); + }; + + const handleContinue = useCallback(async () => { + // Player manually continues to next question + if (currentQuestionIndex < questions.length - 1) { + advanceToNextQuestion(); + } else { + // Level complete - check if player can advance + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Quick Sight Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const advanceToNextQuestion = useCallback(() => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(prev => prev + 1); + setSelectedPosition(null); + setShowFeedback(false); + setIsCorrect(false); + setTimeRemaining(0); + setIsTimerRunning(false); + setShowTargetLetter(true); + setShowSelectionGrid(false); + setShowTimeoutMessage(false); // Reset timeout message for next question + } + }, [currentQuestionIndex, questions.length]); + + const resetGame = () => { + // Reset all game state + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedPosition(null); + setShowFeedback(false); + setIsCorrect(false); + setShowLevelUp(false); + setLevelFailed(false); + setTimeRemaining(0); + setIsTimerRunning(false); + setShowTimeoutMessage(false); // Reset timeout message + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage && selectedLevel !== null) { + // Start new session for the CURRENT level (not previous level) + const session = startSession(gameKey); + + // Generate new questions for the current level + const newQuestions = generateROARRapidVisualQuestions( + selectedLanguage, + currentLevel, // Use currentLevel state, not gameProgress.currentLevel + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + } + + // Reset game completion state + setIsGameComplete(false); + }; + + const retryLevel = () => { + // Complete reset for retry - same as resetGame but ensures fresh start + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedPosition(null); + setShowFeedback(false); + setIsCorrect(false); + setShowLevelUp(false); + setLevelFailed(false); + setTimeRemaining(0); + setIsTimerRunning(false); + setIsGameComplete(false); + + if (selectedLanguage && selectedLevel !== null) { + // Start fresh session for current level + const session = startSession(gameKey); + + // Generate completely new questions for retry + const newQuestions = generateROARRapidVisualQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10 + ); + setQuestions(newQuestions); + } + }; + + const calculateStars = () => { + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (totalCorrect === questions.length) { + const perfectMessage = selectedLanguage === 'te' ? + 'విజువల్ ప్రాసెసింగ్ మాస్టర్ - పరిపూర్ణ వేగ గుర్తింపు!' : + selectedLanguage === 'mr' ? + 'विज्युअल प्रोसेसिंग मास्टर - परिपूर्ण वेग ओळख!' : + 'Visual Processing Master - Perfect Speed Recognition!'; + achievements.push(perfectMessage); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + const speedMessage = selectedLanguage === 'te' ? + 'వేగ డెమన్ - అద్భుత విజువల్ ప్రాసెసింగ్ నైపుణ్యాలు!' : + selectedLanguage === 'mr' ? + 'वेग डेमन - उत्तम विज्युअल प्रोसेसिंग कौशल्य!' : + 'Speed Demon - Great Visual Processing Skills!'; + achievements.push(speedMessage); + } + if (showLevelUp) { + const levelUpMessage = selectedLanguage === 'te' ? + `లెవల్ అప్! ఇప్పుడు లెవల్ ${gameProgress.currentLevel}` : + selectedLanguage === 'mr' ? + `लेव्हल अप! आता लेव्हल ${gameProgress.currentLevel}` : + `Level Up! Now at Level ${gameProgress.currentLevel}`; + achievements.push(levelUpMessage); + } + + // Add level completion achievement + const scorePercentage = (totalCorrect / questions.length) * 100; + if (scorePercentage >= 60) { + const completeMessage = selectedLanguage === 'te' ? + 'లెవల్ పూర్తయింది - తదుపరి సవాలు కోసం సిద్ధంగా ఉంది!' : + selectedLanguage === 'mr' ? + 'लेव्हल पूर्ण झाले - पुढच्या आव्हानासाठी तयार!' : + 'Level Complete - Ready for Next Challenge!'; + achievements.push(completeMessage); + } else { + const attemptedMessage = selectedLanguage === 'te' ? + 'లెవల్ ప్రయత్నించారు - ముందుకు వెళ్లడానికి మరింత సాధన చేయండి!' : + selectedLanguage === 'mr' ? + 'लेव्हल प्रयत्न केले - पुढे जाण्यासाठी अधिक सराव करा!' : + 'Level Attempted - Practice More to Advance!'; + achievements.push(attemptedMessage); + } + + return achievements; + }; + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + { + // Advance to next level by updating the session + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage} (max: ${languageLevels.maxLevels})`); + + // Manually advance the level using the learning progress hook + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL + navigate(`/roar-rapid-visual-game/level/${nextLevel}`); + }} + /> + ); + } + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level selector is requested (after preview) + if (showLevelSelector) { + const availableLevels = getAvailableLevels(); + + return ( + { + setShowPreview(true); + onBack(); + }} // Go back to preview instead of main menu + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Quick Sight" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Quick Sight +

+
+ + Level {currentLevel} • {difficultySettings.complexity} +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Timer - Clock bar with time outside - Hide when time is up or feedback is shown */} + {!showTimeoutMessage && !showFeedback && ( +
+
+
+ +
+
+
+ )} + + {/* Timeout Message - Show when time is up, keep space when hidden during feedback */} + {timeRemaining === 0 && ( +
+
+
+ +
+ {selectedLanguage === 'te' ? 'ముగిసింది!' : + selectedLanguage === 'mr' ? 'संपला!' : + "Time Up!"} +
+
+
+
+ )} + + {/* Game Area */} + +
+
+
+ ); +} + +// Generate ROAR Rapid Visual Processing questions +function generateROARRapidVisualQuestions(language: Language, level: number, complexity: string, count: number = 10): ROARRapidVisualQuestion[] { + // Language-specific letter sets for progressive difficulty - now uses comprehensive data + const getLanguageLetterSets = (language: Language) => { + // Ensure only supported languages are passed (memoryGameDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const allLetters = memoryGameDataLoader.getAllLetters(supportedLanguage); + + // Create progressive subsets based on complexity + const getSubset = (start: number, end: number) => { + return allLetters.slice(start, Math.min(end, allLetters.length)); + }; + + switch (supportedLanguage) { + case 'te': + // For Telugu, use exact level mapping instead of complexity + const levelKey = level.toString(); + const levelLetters = memoryGameDataLoader.getLettersByLevel(supportedLanguage, levelKey); + return { + basic: levelLetters, + intermediate: levelLetters, + advanced: levelLetters, + expert: levelLetters, + master: levelLetters + }; + case 'kn': + // For Kannada, use exact level mapping instead of complexity + const kannadaLevelKey = level.toString(); + const kannadaLevelLetters = memoryGameDataLoader.getLettersByLevel(supportedLanguage, kannadaLevelKey); + return { + basic: kannadaLevelLetters, + intermediate: kannadaLevelLetters, + advanced: kannadaLevelLetters, + expert: kannadaLevelLetters, + master: kannadaLevelLetters + }; + case 'mr': + return { + basic: getSubset(0, 20), // First 20 letters (basic vowels + consonants) + intermediate: getSubset(0, 35), // First 35 letters (includes some matras) + advanced: getSubset(0, 50), // First 50 letters (more matras) + expert: getSubset(0, 70), // First 70 letters (most matras) + master: allLetters // All letters including compound letters + }; + default: // English + return { + basic: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'], + intermediate: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'], + advanced: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'], + expert: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'], + master: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] + }; + } + }; + + const letterSets = getLanguageLetterSets(language); + + const letterSet = letterSets[complexity as keyof typeof letterSets] || letterSets.basic; + + // Error handling: Check if letter set is empty + if (!letterSet || letterSet.length === 0) { + console.error(`No letters available for language: ${language}, complexity: ${complexity}`); + return []; + } + + const questions: ROARRapidVisualQuestion[] = []; + const usedTargets: string[] = []; + + for (let i = 0; i < count; i++) { + // Get a random target letter that hasn't been used + let target; + let attempts = 0; + + do { + target = letterSet[Math.floor(Math.random() * letterSet.length)]; + attempts++; + } while (usedTargets.includes(target) && attempts < 20); + + // Fallback if we can't find a unique target + if (attempts >= 20) { + console.warn(`Could not find unique target after ${attempts} attempts, using first available letter`); + target = letterSet[0]; + } + + usedTargets.push(target); + + // Create 6 letter positions with target in random position + const targetPosition = Math.floor(Math.random() * 6); + const letters = Array(6).fill(''); + + // Place target letter + letters[targetPosition] = target; + + // Fill other positions with random letters (avoiding target) + // For higher levels, use more similar-looking letters to increase difficulty + const similarLetters = getSimilarLetters(target, level, language); + + for (let j = 0; j < 6; j++) { + if (j !== targetPosition) { + let randomLetter; + let distractorAttempts = 0; + + do { + // Higher levels have higher chance of similar-looking letters + if (level >= 7 && Math.random() < 0.4 && similarLetters.length > 0) { + randomLetter = similarLetters[Math.floor(Math.random() * similarLetters.length)]; + } else { + randomLetter = letterSet[Math.floor(Math.random() * letterSet.length)]; + } + distractorAttempts++; + } while ((randomLetter === target || letters.includes(randomLetter)) && distractorAttempts < 10); + + // Fallback if we can't find a unique distractor + if (distractorAttempts >= 10) { + console.warn(`Could not find unique distractor for position ${j}, using first available letter`); + randomLetter = letterSet.find(l => l !== target && !letters.includes(l)) || letterSet[0]; + } + + letters[j] = randomLetter; + } + } + + questions.push({ + target, + letters, + targetPosition, + complexity, + language + }); + } + + return questions; +} + +// Helper function to get similar-looking letters for higher difficulty +function getSimilarLetters(target: string, level: number, language: Language): string[] { + // Only use similar letters for higher levels (7+) + if (level < 7) return []; + + const similarLetterGroups = { + // English similar letters + 'A': ['R', 'H', 'V'], + 'B': ['P', 'R', 'D'], + 'C': ['G', 'O', 'Q'], + 'D': ['B', 'P', 'O'], + 'E': ['F', 'B', 'P'], + 'F': ['E', 'P', 'T'], + 'G': ['C', 'O', 'Q'], + 'H': ['A', 'N', 'M'], + 'I': ['L', 'T', 'J'], + 'J': ['I', 'L', 'T'], + 'K': ['R', 'X', 'Y'], + 'L': ['I', 'T', 'J'], + 'M': ['N', 'H', 'W'], + 'N': ['M', 'H', 'U'], + 'O': ['C', 'G', 'Q'], + 'P': ['B', 'R', 'D'], + 'Q': ['O', 'G', 'C'], + 'R': ['P', 'B', 'K'], + 'S': ['Z', '5', '2'], + 'T': ['I', 'L', 'J'], + 'U': ['V', 'N', 'Y'], + 'V': ['U', 'A', 'Y'], + 'W': ['M', 'V', 'U'], + 'X': ['K', 'Y', 'Z'], + 'Y': ['V', 'U', 'X'], + 'Z': ['S', '2', '7'], + + // Telugu similar letters + 'అ': ['ఆ', 'ఇ', 'ఈ'], + 'ఆ': ['అ', 'ఇ', 'ఈ'], + 'ఇ': ['ఈ', 'అ', 'ఆ'], + 'ఈ': ['ఇ', 'అ', 'ఆ'], + 'ఉ': ['ఊ', 'ఎ', 'ఏ'], + 'ఊ': ['ఉ', 'ఎ', 'ఏ'], + 'ఎ': ['ఏ', 'ఐ', 'ఉ'], + 'ఏ': ['ఎ', 'ఐ', 'ఉ'], + 'ఐ': ['ఎ', 'ఏ', 'ఒ'], + 'ఒ': ['ఓ', 'ఔ', 'ఐ'], + 'ఓ': ['ఒ', 'ఔ', 'ఐ'], + 'ఔ': ['ఒ', 'ఓ', 'ఐ'], + 'క': ['ఖ', 'గ', 'ఘ'], + 'ఖ': ['క', 'గ', 'ఘ'], + 'గ': ['క', 'ఖ', 'ఘ'], + 'ఘ': ['క', 'ఖ', 'గ'], + 'చ': ['ఛ', 'జ', 'ఝ'], + 'ఛ': ['చ', 'జ', 'ఝ'], + 'జ': ['చ', 'ఛ', 'ఝ'], + 'ఝ': ['చ', 'ఛ', 'జ'], + 'ట': ['ఠ', 'డ', 'ఢ'], + 'ఠ': ['ట', 'డ', 'ఢ'], + 'డ': ['ట', 'ఠ', 'ఢ'], + 'ఢ': ['ట', 'ఠ', 'డ'], + 'త': ['థ', 'ద', 'ధ'], + 'థ': ['త', 'ద', 'ధ'], + 'ద': ['త', 'థ', 'ధ'], + 'ధ': ['త', 'థ', 'ద'], + 'న': ['ప', 'ఫ', 'బ'], + 'ప': ['ఫ', 'బ', 'భ'], + 'ఫ': ['ప', 'బ', 'భ'], + 'బ': ['ప', 'ఫ', 'భ'], + 'భ': ['ప', 'ఫ', 'బ'], + 'మ': ['య', 'ర', 'ల'], + 'య': ['ర', 'ల', 'వ'], + 'ర': ['ల', 'వ', 'య'], + 'ల': ['వ', 'య', 'ర'], + 'వ': ['శ', 'ష', 'స'], + 'శ': ['ష', 'స', 'హ'], + 'ష': ['స', 'హ', 'శ'], + 'స': ['హ', 'శ', 'ష'], + 'హ': ['ళ', 'క్ష', 'స'], + + // Marathi similar letters + 'अ': ['आ', 'इ', 'ई'], + 'आ': ['अ', 'इ', 'ई'], + 'इ': ['ई', 'अ', 'आ'], + 'ई': ['इ', 'अ', 'आ'], + 'उ': ['ऊ', 'ए', 'ऐ'], + 'ऊ': ['उ', 'ए', 'ऐ'], + 'ए': ['ऐ', 'ओ', 'औ'], + 'ऐ': ['ए', 'ओ', 'औ'], + 'ओ': ['औ', 'ए', 'ऐ'], + 'औ': ['ओ', 'ए', 'ऐ'], + 'क': ['ख', 'ग', 'घ'], + 'ख': ['क', 'ग', 'घ'], + 'ग': ['क', 'ख', 'घ'], + 'घ': ['क', 'ख', 'ग'], + 'च': ['छ', 'ज', 'झ'], + 'छ': ['च', 'ज', 'झ'], + 'ज': ['च', 'छ', 'झ'], + 'झ': ['च', 'छ', 'ज'], + 'ट': ['ठ', 'ड', 'ढ'], + 'ठ': ['ट', 'ड', 'ढ'], + 'ड': ['ट', 'ठ', 'ढ'], + 'ढ': ['ट', 'ठ', 'ड'], + 'त': ['थ', 'द', 'ध'], + 'थ': ['त', 'द', 'ध'], + 'द': ['त', 'थ', 'ध'], + 'ध': ['त', 'थ', 'द'], + 'न': ['प', 'फ', 'ब'], + 'प': ['फ', 'ब', 'भ'], + 'फ': ['प', 'ब', 'भ'], + 'ब': ['प', 'फ', 'भ'], + 'भ': ['प', 'फ', 'ब'], + 'म': ['य', 'र', 'ल'], + 'य': ['र', 'ल', 'व'], + 'र': ['ल', 'व', 'य'], + 'ल': ['व', 'य', 'र'], + 'व': ['श', 'ष', 'स'], + 'श': ['ष', 'स', 'ह'], + 'ष': ['स', 'ह', 'श'], + 'स': ['ह', 'श', 'ष'], + 'ह': ['ळ', 'क्ष', 'स'] + }; + + return similarLetterGroups[target as keyof typeof similarLetterGroups] || []; +} diff --git a/src/lib/axl-explorations/src/components/games/ROARRapidVisualGameCore.tsx b/src/lib/axl-explorations/src/components/games/ROARRapidVisualGameCore.tsx new file mode 100644 index 00000000..9cf16ad6 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARRapidVisualGameCore.tsx @@ -0,0 +1,233 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { Timer } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { ContinueButton } from "./ContinueButton"; + +export interface ROARRapidVisualQuestion { + target: string; + letters: string[]; + targetPosition: number; + complexity: string; + language: Language; +} + +interface ROARRapidVisualGameCoreProps { + // Question data + currentQuestion: ROARRapidVisualQuestion; + + // Game state + mode: 'game' | 'preview'; + selectedLanguage: Language; + + // Timer state + timeRemaining: number; + isTimerRunning: boolean; + + // UI state + showTargetLetter: boolean; + showSelectionGrid: boolean; + showFeedback: boolean; + isCorrect: boolean; + selectedPosition: number | null; + + // Preview-specific state + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + + // Handlers + onPositionSelect: (position: number) => void; + onContinue?: () => void; + + // Styling + className?: string; +} + +export function ROARRapidVisualGameCore({ + currentQuestion, + mode, + selectedLanguage, + timeRemaining, + isTimerRunning, + showTargetLetter, + showSelectionGrid, + showFeedback, + isCorrect, + selectedPosition, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onPositionSelect, + onContinue, + className = '' +}: ROARRapidVisualGameCoreProps) { + const gridRef = useRef(null); + + // Get localized text + const getLocalizedText = (key: string) => { + const texts = { + findLetter: { + en: 'Find the Letter', + te: 'అక్షరాన్ని కనుగొనండి', + kn: 'ಅಕ್ಷರವನ್ನು ಹುಡುಕಿ', + mr: 'अक्षर शोधा' + }, + correct: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿ!', + mr: '🎉 बरोबर!' + }, + wrong: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + }, + time: { + en: 'Time', + te: 'సమయం', + kn: 'ಸಮಯ', + mr: 'वेळ' + }, + finishLevel: { + en: 'Finish Level', + te: 'లెవల్ పూర్తి చేయండి', + kn: 'ಮಟ್ಟವನ್ನು ಮುಗಿಸಿ', + mr: 'पातळी पूर्ण करा' + } + }; + + return texts[key as keyof typeof texts]?.[selectedLanguage] || texts[key as keyof typeof texts]?.en || ''; + }; + + return ( +
+ {/* Top Section - Question/Target */} + {showTargetLetter ? ( + /* Target Letter Display Phase */ +
+
+ {/* Timer for preview mode - positioned like main game */} + {isPreview && isTimerRunning && ( +
+
+
+ +
+
+
+ )} + +
+
🔍
+
+
+ + {currentQuestion.target} + +
+
+
+
+
+ ) : ( + /* Selection Phase */ +
+
+

+ {getLocalizedText("findLetter")} +

+
+ + {/* Visual Processing Grid */} +
+
+ {/* Hand Pointer for preview mode */} + {mode === "preview" && showHandPointer && !showFeedback && ( +
+
+ 👆 +
+
+ )} + +
+ {currentQuestion.letters.map((letter, index) => ( + + ))} +
+
+
+
+ )} + + {/* Feedback - Fixed Height Area */} +
+ {showFeedback && ( + <> + {isCorrect ? ( +
+

+ {getLocalizedText("correct")} +

+
+ ) : ( +
+

+ {getLocalizedText("wrong")} +

+
+ )} + + {/* Continue Button - only show in game mode */} + + + )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/ROARRapidVisualGamePreview.tsx b/src/lib/axl-explorations/src/components/games/ROARRapidVisualGamePreview.tsx new file mode 100644 index 00000000..c4d8a0de --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARRapidVisualGamePreview.tsx @@ -0,0 +1,649 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Zap, Sparkles, CheckCircle, Gamepad2, RotateCcw, Eye, Timer } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { ClockwiseTimer } from "../ClockwiseTimer"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { ROARRapidVisualGameCore, type ROARRapidVisualQuestion } from "./ROARRapidVisualGameCore"; + +interface ROARRapidVisualGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" button + | 'showTarget' // Show target letter with timer + | 'instruction2' // After timer, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show grid, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Quick Sight", + description: "Remember the target letter and find its position in the grid!", + steps: [ + "👁️ Look at the target letter carefully", + "⏱️ Remember it before time runs out", + "🔍 Find the letter position in the grid", + "✨ Click the correct position!" + ], + instruction1: "Get ready! You'll see a target letter. Click 'I'm Ready' when you're prepared", + instruction2: "Good! Now remember where that letter was", + instruction3: "Now find the target letter in the grid below", + instruction4: "Excellent! You found the correct position!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + readyButton: "I'm Ready", + narration1: "Get ready! You'll see a target letter. Click I'm Ready when you're prepared", + narration2: "Good! Now remember where that letter was", + narration3: "Now find the target letter in the grid below", + narration4: "Excellent! You found the correct position!", + howToPlay: "How to Play", + rememberText: "Remember this letter!", + findPositionText: "Find the Letter Position", + demo: { + target: "A", + options: ["A", "B", "C", "D", "E", "F"], + correctPosition: 0, + explanation: "The letter 'A' was at position 1!" + } + }, + te: { + title: "క్విక్‌సైట్", + description: "లక్ష్య అక్షరాన్ని గుర్తుంచుకోండి మరియు గ్రిడ్‌లో దాని స్థానాన్ని కనుగొనండి!", + steps: [ + "👁️ లక్ష్య అక్షరాన్ని జాగ్రత్తగా చూడండి", + "⏱️ సమయం ముగిసే ముందు దానిని గుర్తుంచుకోండి", + "🔍 గ్రిడ్‌లో అక్షర స్థానాన్ని కనుగొనండి", + "✨ సరైన స్థానాన్ని క్లిక్ చేయండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు లక్ష్య అక్షరాన్ని చూస్తారు. మీరు సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఆ అక్షరం ఎక్కడ ఉందో గుర్తుంచుకోండి", + instruction3: "ఇప్పుడు క్రింది గ్రిడ్‌లో లక్ష్య అక్షరాన్ని కనుగొనండి", + instruction4: "అద్భుతం! మీరు సరైన స్థానాన్ని కనుగొన్నారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + readyButton: "నేను సిద్ధంగా ఉన్నాను", + narration1: "సిద్ధంగా ఉండండి! మీరు లక్ష్య అక్షరాన్ని చూస్తారు. మీరు సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఆ అక్షరం ఎక్కడ ఉందో గుర్తుంచుకోండి", + narration3: "ఇప్పుడు క్రింది గ్రిడ్‌లో లక్ష్య అక్షరాన్ని కనుగొనండి", + narration4: "అద్భుతం! మీరు సరైన స్థానాన్ని కనుగొన్నారు!", + howToPlay: "ఎలా ఆడాలి", + rememberText: "ఈ అక్షరాన్ని గుర్తుంచుకోండి!", + findPositionText: "అక్షర స్థానాన్ని కనుగొనండి", + demo: { + target: "అ", + options: ["అ", "ఆ", "ఇ", "ఈ", "ఉ", "ఊ"], + correctPosition: 0, + explanation: "అక్షరం 'అ' స్థానం 1లో ఉంది!" + } + }, + kn: { + title: "ಕ್ವಿಕ್‌ಸೈಟ್", + description: "ಗುರಿ ಅಕ್ಷರವನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ ಮತ್ತು ಗ್ರಿಡ್‌ನಲ್ಲಿ ಅದರ ಸ್ಥಾನವನ್ನು ಹುಡುಕಿ!", + steps: [ + "👁️ ಗುರಿ ಅಕ್ಷರವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ನೋಡಿ", + "⏱️ ಸಮಯ ಮುಗಿಯುವ ಮೊದಲು ಅದನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + "🔍 ಗ್ರಿಡ್‌ನಲ್ಲಿ ಅಕ್ಷರ ಸ್ಥಾನವನ್ನು ಹುಡುಕಿ", + "✨ ಸರಿಯಾದ ಸ್ಥಾನವನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ!" + ], + instruction1: "ತಯಾರಾಗಿ! ನೀವು ಗುರಿ ಅಕ್ಷರವನ್ನು ನೋಡುತ್ತೀರಿ. ನೀವು ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಆ ಅಕ್ಷರ ಎಲ್ಲಿತ್ತು ಎಂದು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + instruction3: "ಈಗ ಕೆಳಗಿನ ಗ್ರಿಡ್‌ನಲ್ಲಿ ಗುರಿ ಅಕ್ಷರವನ್ನು ಹುಡುಕಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಸರಿಯಾದ ಸ್ಥಾನವನ್ನು ಕಂಡುಕೊಂಡಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + readyButton: "ನಾನು ಸಿದ್ಧ", + narration1: "ತಯಾರಾಗಿ! ನೀವು ಗುರಿ ಅಕ್ಷರವನ್ನು ನೋಡುತ್ತೀರಿ. ನೀವು ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಆ ಅಕ್ಷರ ಎಲ್ಲಿತ್ತು ಎಂದು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ", + narration3: "ಈಗ ಕೆಳಗಿನ ಗ್ರಿಡ್‌ನಲ್ಲಿ ಗುರಿ ಅಕ್ಷರವನ್ನು ಹುಡುಕಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಸರಿಯಾದ ಸ್ಥಾನವನ್ನು ಕಂಡುಕೊಂಡಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + rememberText: "ಈ ಅಕ್ಷರವನ್ನು ನೆನಪಿಟ್ಟುಕೊಳ್ಳಿ!", + findPositionText: "ಅಕ್ಷರ ಸ್ಥಾನವನ್ನು ಹುಡುಕಿ", + demo: { + target: "ಅ", + options: ["ಅ", "ಆ", "ಇ", "ಈ", "ಉ", "ಊ"], + correctPosition: 0, + explanation: "ಅಕ್ಷರ 'ಅ' ಸ್ಥಾನ 1ರಲ್ಲಿ ಇತ್ತು!" + } + }, + mr: { + title: "क्विकसाइट", + description: "लक्ष्य अक्षर लक्षात ठेवा आणि ग्रिडमध्ये त्याचे स्थान शोधा!", + steps: [ + "👁️ लक्ष्य अक्षर काळजीपूर्वक पहा", + "⏱️ वेळ संपण्यापूर्वी ते लक्षात ठेवा", + "🔍 ग्रिडमध्ये अक्षर स्थान शोधा", + "✨ योग्य स्थानावर क्लिक करा!" + ], + instruction1: "तयार व्हा! तुम्हाला लक्ष्य अक्षर दिसेल. तुम्ही तयार असाल तेव्हा 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता ते अक्षर कुठे होते ते लक्षात ठेवा", + instruction3: "आता खालील ग्रिडमध्ये लक्ष्य अक्षर शोधा", + instruction4: "उत्कृष्ट! तुम्हाला योग्य स्थान सापडले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + readyButton: "मी तयार आहे", + narration1: "तयार व्हा! तुम्हाला लक्ष्य अक्षर दिसेल. तुम्ही तयार असाल तेव्हा मी तयार आहे क्लिक करा", + narration2: "चांगले! आता ते अक्षर कुठे होते ते लक्षात ठेवा", + narration3: "आता खालील ग्रिडमध्ये लक्ष्य अक्षर शोधा", + narration4: "उत्कृष्ट! तुम्हाला योग्य स्थान सापडले!", + howToPlay: "कसे खेळायचे", + rememberText: "हे अक्षर लक्षात ठेवा!", + findPositionText: "अक्षर स्थान शोधा", + demo: { + target: "अ", + options: ["अ", "आ", "इ", "ई", "उ", "ऊ"], + correctPosition: 0, + explanation: "अक्षर 'अ' स्थान 1 वर होते!" + } + } +}; + +function ROARRapidVisualGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: ROARRapidVisualGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showTargetLetter, setShowTargetLetter] = useState(false); + const [timeRemaining, setTimeRemaining] = useState(3); + const [isTimerRunning, setIsTimerRunning] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const gridRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question for preview + const demoQuestion: ROARRapidVisualQuestion = { + target: instructions.demo.target, + letters: instructions.demo.options, + targetPosition: instructions.demo.correctPosition, + complexity: 'easy', + language: contentLanguage + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined letter games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined letter games audio files for Quick Sight + const gameName = 'Combined Letter Games'; + const subGame = 'Quick Sight'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + case 'showTarget': + setCurrentStep(0); + break; + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setSelectedAnswer(null); + setShowFeedback(false); + setShowTargetLetter(false); + setTimeRemaining(3); + setIsTimerRunning(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + // Focus on ready button after a brief delay + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Timer for showing target letter + useEffect(() => { + if (showTargetLetter && isTimerRunning) { + const timer = setInterval(() => { + setTimeRemaining(prev => { + if (prev <= 1) { + setIsTimerRunning(false); + return 0; + } + return prev - 1; + }); + }, 1000); + + return () => clearInterval(timer); + } + }, [showTargetLetter, isTimerRunning]); + + // When timer reaches 0, hide target and show grid (disabled) + useEffect(() => { + if (showTargetLetter && !isTimerRunning && timeRemaining === 0) { + const handleTimerEnd = async () => { + // Hide target letter and show grid immediately (but disabled) + setShowTargetLetter(false); + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + // After instruction 2, show instruction 3 + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + // Move to waiting for answer - now enable grid + setDemoStep('waitForAnswer'); + + // Focus on grid container + setTimeout(() => { + gridRef.current?.focus(); + }, 100); + }; + + handleTimerEnd(); + } + }, [showTargetLetter, isTimerRunning, timeRemaining]); + + // Handle ready button click + const handleReadyClick = () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + setDemoStep('showTarget'); + setShowTargetLetter(true); + setTimeRemaining(3); + setIsTimerRunning(true); + }; + + // Handle option click + const handleOptionClick = async (position: number) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + setSelectedAnswer(position); + setShowFeedback(true); + + const isCorrect = position === instructions.demo.correctPosition; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + // Show instruction 4 and play its narration + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + // Mark demo run as complete and increment completion count + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + // Reset feedback after a delay to allow retry + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + }; + + // Restart demo (for second run or Help replay) + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowTargetLetter(false); + setTimeRemaining(3); + setIsTimerRunning(false); + setCurrentStep(0); + }; + + // Help button click - replay demo without affecting success count + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + // Show ready button + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + + // Show grid options - show after target letter disappears (timer ends), but enable only after instructions + const showGrid = demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + + // Grid is enabled only when waiting for answer + const gridEnabled = demoStep === 'waitForAnswer'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Start game handler + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Fixed Layout Structure */} +
+ + {/* Ready Button Section - Always visible but disabled initially */} + {showReadyButton && !showTargetLetter && !showGrid && ( +
+
+ +
+ + {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {/* Demo Game UI */} + {(showTargetLetter || showGrid) && ( +
+ +
+ )} + +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} + +export default ROARRapidVisualGamePreview; diff --git a/src/lib/axl-explorations/src/components/games/ROARWordGame.tsx b/src/lib/axl-explorations/src/components/games/ROARWordGame.tsx new file mode 100644 index 00000000..909952c3 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARWordGame.tsx @@ -0,0 +1,616 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import ROARWordGamePreview from "./ROARWordGamePreview"; +import { ROARWordGameCore, type ROARWordQuestion } from "./ROARWordGameCore"; +import { ArrowLeft, ArrowRight, RotateCcw, TrendingUp, Sparkles, BookOpen, Globe, Trash2 } from "lucide-react"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { wordDetectiveDataLoader } from "../../utils/wordDetectiveDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; + +interface ROARWordGameProps { + onBack: () => void; +} + +export function ROARWordGame({ onBack }: ROARWordGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + // Extend the core interface for game-specific properties + interface ExtendedROARWordQuestion extends ROARWordQuestion { + // Add any additional properties if needed + } + + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + + // ✅ CHILD-FRIENDLY: Track used questions to prevent repetition + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations - All languages have 10 levels + const getLanguageLevels = (language: Language) => { + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `wordDetective_${selectedLanguage}` : 'wordDetective'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Initialize game session and questions when language or level is selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + await new Promise(resolve => setTimeout(resolve, 100)); + + console.log(`Starting session for ${selectedLanguage}, level: ${currentLevel}, key: ${gameKey}`); + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + // Ensure only supported languages are passed (wordDetectiveDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const generatedQuestions = wordDetectiveDataLoader.generateWordQuestions( + supportedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + usedQuestions + ); + + // Ensure compatibility with ROARWordGameCore interface + const newQuestions: ExtendedROARWordQuestion[] = generatedQuestions.map(q => ({ + ...q, + language: selectedLanguage + })); + setQuestions(newQuestions); + console.log(`Generated ${newQuestions.length} questions for level ${currentLevel}`); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute backendCurrentLevel from backend stats for true "has progress" flow + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + const computedNextLevel = Math.min(Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), languageLevels.maxLevels); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min(Math.max(computedNextLevel, backendProvided), languageLevels.maxLevels); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setShowPreview(false); // Hide preview when level is selected + setLevelFailed(false); + setUsedQuestions(new Set()); + } + }, [selectedLevel, selectedLanguage]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + + + + + + const handleAnswerSelect = async (isReal: boolean) => { + if (showFeedback) return; + + setSelectedAnswer(isReal); + const correct = isReal === currentQuestion.isReal; + setIsCorrect(correct); + setShowFeedback(true); + + // Record the answer for adaptive learning + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `worddetective_${currentLevel}_${currentQuestionIndex}`; + // Include the word in correct answer + const correctAnswerForTelemetry = `${currentQuestion.word}: ${currentQuestion.isReal ? 'real' : 'fake'}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'wordDetective', + `${currentQuestion.word}: ${isReal ? 'real' : 'fake'}`, + correctAnswerForTelemetry, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'wordDetective', + userAnswer: String(isReal), + correctAnswer: correctAnswerForTelemetry, + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 1); + setTotalCorrect(prevTotal => prevTotal + 1); + } + + // Don't auto-advance - player must manually continue + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedAnswer(null); + setShowFeedback(false); + } else { + // Level complete + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Word Detective Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + console.log(`Game completed for ${selectedLanguage}, previous level: ${previousLevel}`); + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const handleBackClick = async () => { + await sessionTelemetryManager.endSubSessionWithBackButton(); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + // ✅ CHILD-FRIENDLY: Reset session cache for completely fresh questions + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + // Start new session and regenerate questions + if (selectedLanguage) { + const session = startSession(gameKey); + // Ensure only supported languages are passed (wordDetectiveDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const generatedQuestions = wordDetectiveDataLoader.generateWordQuestions( + supportedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() // Fresh start with no used questions + ); + + // Ensure compatibility with ROARWordGameCore interface + const newQuestions: ExtendedROARWordQuestion[] = generatedQuestions.map(q => ({ + ...q, + language: selectedLanguage + })); + setQuestions(newQuestions); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/roar-word-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/roar-word-game'); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; // Prevent division by zero + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { // Only calculate achievements if there are questions + if (totalCorrect === questions.length) { + achievements.push("Word Master - Perfect Validation!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Word Detective - Great Recognition!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selection screen if level not selected (after preview) + if (showLevelSelector) { + // For level selector, use the actual progress level, not selectedLevel + const levelSelectorCurrentLevel = gameProgress.currentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} // Go back to preview instead of main menu + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Word Detective" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + { + // ✅ MANUAL LEVEL ADVANCEMENT: Force advance to next level when user clicks "Next Level" + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage} (max: ${languageLevels.maxLevels})`); + + // Manually advance the level using the learning progress hook + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL + navigate(`/roar-word-game/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Word Detective +

+
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/ROARWordGameCore.tsx b/src/lib/axl-explorations/src/components/games/ROARWordGameCore.tsx new file mode 100644 index 00000000..8febd2d7 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARWordGameCore.tsx @@ -0,0 +1,164 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { Check, X } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { ContinueButton } from "./ContinueButton"; + +export interface ROARWordQuestion { + word: string; + isReal: boolean; + complexity: string; + language: Language; +} + +export interface ROARWordGameCoreProps { + currentQuestion: ROARWordQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + showFeedback?: boolean; + isCorrect?: boolean; + selectedAnswer?: boolean | null; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onAnswerSelect: (isReal: boolean) => void; + onContinue?: () => void; + className?: string; +} + +export function ROARWordGameCore({ + currentQuestion, + mode, + selectedLanguage, + showFeedback = false, + isCorrect = false, + selectedAnswer = null, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onAnswerSelect, + onContinue, + className = '' +}: ROARWordGameCoreProps) { + const buttonsRef = useRef(null); + + // Get localized text + const getLocalizedText = (key: string) => { + const texts = { + isThisRealWord: { + en: 'Is this a real word?', + te: 'ఇది నిజమైన పదమా?', + kn: 'ಇದು ನಿಜವಾದ ಪದವೇ?', + mr: 'हा खरा शब्द आहे का?' + }, + correctMessage: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿ!', + mr: '🎉 बरोबर!' + }, + wrongMessage: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + + return texts[key as keyof typeof texts]?.[selectedLanguage] || texts[key as keyof typeof texts]?.en || ''; + }; + + return ( +
+
+ {/* Word Display */} +
+ {isPreview ? ( +
+

+ {currentQuestion.word} +

+
+ ) : ( +

+ {currentQuestion.word} +

+ )} +
+ + {/* Action Buttons */} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+ + + +
+
+ + {/* Feedback Area - Show in both game and preview modes */} + {showFeedback && ( +
+
+

+ {isCorrect ? getLocalizedText('correctMessage') : getLocalizedText('wrongMessage')} +

+
+ + {/* Continue Button - Only show in game mode */} + +
+ )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/ROARWordGamePreview.tsx b/src/lib/axl-explorations/src/components/games/ROARWordGamePreview.tsx new file mode 100644 index 00000000..a6a75ab2 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/ROARWordGamePreview.tsx @@ -0,0 +1,622 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, BookOpen, Sparkles, Clock, CheckCircle, Trash2, Gamepad2, RotateCcw, Eye } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { ROARWordGameCore, type ROARWordQuestion } from "./ROARWordGameCore"; + +interface ROARWordGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "Show Word" button + | 'showWord' // Show the word + | 'instruction2' // After showing word, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show buttons, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Word Detective", + description: "Identify real words and detect fake ones!", + steps: [ + "📖 Read the word carefully", + "🤔 Think if it's a real word", + "✅ Click Book for real words", + "🗑️ Click Trash for fake words" + ], + instruction1: "Get ready! You'll see a word. Click 'Show Word' when you're prepared", + instruction2: "Good! Now read this word carefully", + instruction3: "Decide: Is this a real word or a fake word?", + instruction4: "Perfect! You identified the word correctly!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + showWordButton: "Show Word", + realWordLabel: "Real Word", + fakeWordLabel: "Fake Word", + narration1: "Get ready! You'll see a word. Click Show Word when you're prepared", + narration2: "Good! Now read this word carefully", + narration3: "Decide: Is this a real word or a fake word?", + narration4: "Perfect! You identified the word correctly!", + howToPlay: "How to Play", + isThisRealWord: "Is this a real word?", + demo: { + word: "CAT", + isReal: true, + explanation: "CAT is a real word - it's an animal!" + } + }, + te: { + title: "పద దర్యాప్తుడు", + description: "నిజమైన పదాలను గుర్తించండి మరియు నకిలీవాటిని గుర్తించండి!", + steps: [ + "📖 పదాన్ని జాగ్రత్తగా చదవండి", + "🤔 అది నిజమైన పదమా అని ఆలోచించండి", + "✅ నిజమైన పదాలకు Book క్లిక్ చేయండి", + "🗑️ నకిలీ పదాలకు Trash క్లిక్ చేయండి" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు ఒక పదాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు 'పదం చూపించు' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + instruction3: "నిర్ణయించండి: ఇది నిజమైన పదమా లేదా నకిలీ పదమా?", + instruction4: "పర్ఫెక్ట్! మీరు పదాన్ని సరిగ్గా గుర్తించారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + showWordButton: "పదం చూపించు", + realWordLabel: "నిజమైన పదం", + fakeWordLabel: "నకిలీ పదం", + narration1: "సిద్ధంగా ఉండండి! మీరు ఒక పదాన్ని చూస్తారు. సిద్ధంగా ఉన్నప్పుడు పదం చూపించు క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ పదాన్ని జాగ్రత్తగా చదవండి", + narration3: "నిర్ణయించండి: ఇది నిజమైన పదమా లేదా నకిలీ పదమా?", + narration4: "పర్ఫెక్ట్! మీరు పదాన్ని సరిగ్గా గుర్తించారు!", + howToPlay: "ఎలా ఆడాలి", + isThisRealWord: "ఇది నిజమైన పదమా?", + demo: { + word: "పిల్లి", + isReal: true, + explanation: "పిల్లి ఒక నిజమైన పదం - ఇది ఒక జంతువు!" + } + }, + kn: { + title: "ಪದ ತನಿಖೆಗಾರ", + description: "ನಿಜವಾದ ಪದಗಳನ್ನು ಗುರುತಿಸಿ ಮತ್ತು ನಕಲಿಗಳನ್ನು ಪತ್ತೆ ಮಾಡಿ!", + steps: [ + "📖 ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "🤔 ಅದು ನಿಜವಾದ ಪದವೇ ಎಂದು ಯೋಚಿಸಿ", + "✅ ನಿಜವಾದ ಪದಗಳಿಗೆ Book ಕ್ಲಿಕ್ ಮಾಡಿ", + "🗑️ ನಕಲಿ ಪದಗಳಿಗೆ Trash ಕ್ಲಿಕ್ ಮಾಡಿ" + ], + instruction1: "ತಯಾರಾಗಿ! ನೀವು ಒಂದು ಪದವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ಪದ ತೋರಿಸಿ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ನಿರ್ಧರಿಸಿ: ಇದು ನಿಜವಾದ ಪದವೇ ಅಥವಾ ನಕಲಿ ಪದವೇ?", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾಗಿ ಗುರುತಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + showWordButton: "ಪದ ತೋರಿಸಿ", + realWordLabel: "ನಿಜವಾದ ಪದ", + fakeWordLabel: "ನಕಲಿ ಪದ", + narration1: "ತಯಾರಾಗಿ! ನೀವು ಒಂದು ಪದವನ್ನು ನೋಡುತ್ತೀರಿ. ಸಿದ್ಧರಾದಾಗ ಪದ ತೋರಿಸಿ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಪದವನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ನಿರ್ಧರಿಸಿ: ಇದು ನಿಜವಾದ ಪದವೇ ಅಥವಾ ನಕಲಿ ಪದವೇ?", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದವನ್ನು ಸರಿಯಾಗಿ ಗುರುತಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + isThisRealWord: "ಇದು ನಿಜವಾದ ಪದವೇ?", + demo: { + word: "ಬೆಕ್ಕು", + isReal: true, + explanation: "ಬೆಕ್ಕು ಒಂದು ನಿಜವಾದ ಪದ - ಇದು ಒಂದು ಪ್ರಾಣಿ!" + } + }, + mr: { + title: "शब्द तपासणी", + description: "खरे शब्द ओळखा आणि खोटे शब्द शोधा!", + steps: [ + "📖 शब्द काळजीपूर्वक वाचा", + "🤔 तो खरा शब्द आहे का याचा विचार करा", + "✅ खऱ्या शब्दांसाठी Book क्लिक करा", + "🗑️ नकली शब्दांसाठी Trash क्लिक करा" + ], + instruction1: "तयार व्हा! तुम्हाला एक शब्द दिसेल. तयार असाल तेव्हा 'शब्द दाखवा' क्लिक करा", + instruction2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + instruction3: "ठरवा: हा खरा शब्द आहे की नकली शब्द?", + instruction4: "उत्कृष्ट! तुम्ही शब्द योग्यरित्या ओळखला!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + showWordButton: "शब्द दाखवा", + realWordLabel: "खरा शब्द", + fakeWordLabel: "नकली शब्द", + narration1: "तयार व्हा! तुम्हाला एक शब्द दिसेल. तयार असाल तेव्हा शब्द दाखवा क्लिक करा", + narration2: "चांगले! आता हा शब्द काळजीपूर्वक वाचा", + narration3: "ठरवा: हा खरा शब्द आहे की नकली शब्द?", + narration4: "उत्कृष्ट! तुम्ही शब्द योग्यरित्या ओळखला!", + howToPlay: "कसे खेळायचे", + isThisRealWord: "हा खरा शब्द आहे का?", + demo: { + word: "मांजर", + isReal: true, + explanation: "मांजर हा खरा शब्द आहे - हे एक प्राणी आहे!" + } + } +}; + +export function ROARWordGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: ROARWordGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showWord, setShowWord] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const buttonsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question object for ROARWordGameCore + const demoQuestion: ROARWordQuestion = { + word: instructions.demo.word, + isReal: instructions.demo.isReal, + complexity: 'easy', + language: contentLanguage + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined word games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined word games audio files for Word Detective + const gameName = 'Combined Word Games'; + const subGame = 'Word Detective'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + case 'showWord': + setCurrentStep(0); + break; + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setSelectedAnswer(null); + setShowFeedback(false); + setShowWord(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle ready button click + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + setDemoStep('showWord'); + setShowWord(true); + + // Wait a moment, then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + // After instruction 2, show instruction 3 + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + // Move to waiting for answer + setDemoStep('waitForAnswer'); + + setTimeout(() => { + buttonsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle answer button click + const handleAnswerClick = async (isReal: boolean) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + setSelectedAnswer(isReal); + setShowFeedback(true); + + const isCorrect = isReal === instructions.demo.isReal; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowWord(false); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showButtons = demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + setTimeout(() => { + onStartGame(); + }, 100); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + setTimeout(() => { + onBack(); + }, 100); + }; + + // Start game handler + const handleStartGame = () => { + stopAllAudio(); + setTimeout(() => { + onStartGame(); + }, 100); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Fixed Layout Structure */} +
+ {/* Ready Button Section - Always visible but disabled initially */} + {showReadyButton && !showWord && !showButtons && ( +
+
+ +
+ + {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {showWord && !showButtons && ( +
+

+ {instructions.demo.word} +

+
+ )} + + {showButtons && ( + <> + + + {/* Feedback for Word Detective - below the buttons */} + {/* {showFeedback && ( +
+
+

+ {isCorrectAnswer ? '🎉 Correct!' : '😢 Oops! Wrong!'} +

+
+
+ )} */} + + )} + +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} + +export default ROARWordGamePreview; diff --git a/src/lib/axl-explorations/src/components/games/SentenceGame.tsx b/src/lib/axl-explorations/src/components/games/SentenceGame.tsx new file mode 100644 index 00000000..dafbbc55 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/SentenceGame.tsx @@ -0,0 +1,847 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { ArrowLeft, ArrowRight, RotateCcw, CheckCircle, TrendingUp, Globe, Sparkles } from "lucide-react"; +import { cn } from "../../lib/utils"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { loadSentenceData, type Language as SentenceDataLanguage, type SentenceData } from "../../utils/sentenceDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import { SentenceGamePreview } from "./SentenceGamePreview"; +import { SentenceGameCore, type SentenceQuestion } from "./SentenceGameCore"; + +// SentenceQuestion interface is now imported from SentenceGameCore + +interface SentenceGameProps { + onBack: () => void; +} + +export function SentenceGame({ onBack }: SentenceGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [arrangedWords, setArrangedWords] = useState([]); + const [availableWords, setAvailableWords] = useState([]); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + const [levelFailed, setLevelFailed] = useState(false); + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Drag and drop state + const [draggedElement, setDraggedElement] = useState<{word: string, index: number, type: 'available' | 'arranged'} | null>(null); + const [touchStartPos, setTouchStartPos] = useState<{x: number, y: number} | null>(null); + + // Language-specific level configurations (using English names for all languages) + const getLanguageLevels = (language: Language) => { + switch (language) { + case 'te': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + case 'mr': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + case 'kn': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + default: + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + } + }; + + // Use language-specific game key for progress tracking + const gameKey = selectedLanguage ? `sentenceBuilder_${selectedLanguage}` : 'sentenceBuilder'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Initialize game session and questions when language or level is selected + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + await new Promise(resolve => setTimeout(resolve, 100)); + + console.log(`Starting sentence game session for ${selectedLanguage}, level: ${currentLevel}`); + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + const newQuestions = generateSentenceQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + usedQuestions + ); + setQuestions(newQuestions); + console.log(`Generated ${newQuestions.length} sentence questions for level ${currentLevel}`); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute effective current level from progress (score > 0 or completed) + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + + const computedFromProgress = Math.min( + Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), + languageLevels.maxLevels + ); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min( + Math.max(computedFromProgress, backendProvided), + languageLevels.maxLevels + ); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setArrangedWords([]); + setAvailableWords([]); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + setShowPreview(false); // Hide preview when level is selected + setUsedQuestions(new Set()); + } + }, [selectedLevel, selectedLanguage]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + useEffect(() => { + if (currentQuestion) { + // Shuffle words initially + const shuffled = [...currentQuestion.words].sort(() => Math.random() - 0.5); + setAvailableWords(shuffled); + setArrangedWords([]); + } + }, [currentQuestion]); + + + // Get localized instruction text + const getInstructionText = (sentence: string, language: Language): string => { + switch (language) { + case 'te': + return `ఈ వాక్యాన్ని సరిగ్గా అమర్చండి: ${sentence}`; + case 'mr': + return `हे वाक्य योग्य क्रमाने लावा: ${sentence}`; + default: + return `Listen carefully and build this sentence: ${sentence}`; + } + }; + + + const addWordToSentence = (word: string, index: number) => { + setArrangedWords([...arrangedWords, word]); + setAvailableWords(availableWords.filter((_, i) => i !== index)); + }; + + const removeWordFromSentence = (index: number) => { + const word = arrangedWords[index]; + setAvailableWords([...availableWords, word]); + setArrangedWords(arrangedWords.filter((_, i) => i !== index)); + }; + + // Drag and drop handlers + const handleDragStart = (e: React.DragEvent, word: string, index: number) => { + e.dataTransfer.setData('text/plain', JSON.stringify({ word, index })); + e.dataTransfer.effectAllowed = 'move'; + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + const data = JSON.parse(e.dataTransfer.getData('text/plain')); + const { word, index } = data; + + // Only add if the word is still in available words + if (availableWords[index] === word) { + addWordToSentence(word, index); + } + }; + + const handleWordDragStart = (e: React.DragEvent, word: string, index: number) => { + e.dataTransfer.setData('text/plain', JSON.stringify({ word, index })); + e.dataTransfer.effectAllowed = 'move'; + }; + + const handleWordDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleWordDrop = (e: React.DragEvent, targetIndex: number) => { + e.preventDefault(); + const data = JSON.parse(e.dataTransfer.getData('text/plain')); + const { word, index } = data; + + // If dropping on an existing word, replace it + if (targetIndex !== undefined) { + const newArrangedWords = [...arrangedWords]; + const removedWord = newArrangedWords[targetIndex]; + newArrangedWords[targetIndex] = word; + setArrangedWords(newArrangedWords); + + // Add the removed word back to available words + if (removedWord) { + setAvailableWords([...availableWords, removedWord]); + } + + // Remove the dragged word from available words + setAvailableWords(availableWords.filter((_, i) => i !== index)); + } + }; + + // Mobile touch handlers + const handleTouchStart = (e: React.TouchEvent, word: string, index: number, type: 'available' | 'arranged') => { + const touch = e.touches[0]; + setTouchStartPos({ x: touch.clientX, y: touch.clientY }); + setDraggedElement({ word, index, type }); + e.preventDefault(); + }; + + const handleTouchMove = (e: React.TouchEvent) => { + if (!draggedElement || !touchStartPos) return; + + const touch = e.touches[0]; + const deltaX = Math.abs(touch.clientX - touchStartPos.x); + const deltaY = Math.abs(touch.clientY - touchStartPos.y); + + // Only start drag if moved more than 10px + if (deltaX > 10 || deltaY > 10) { + e.preventDefault(); + } + }; + + const handleTouchEnd = (e: React.TouchEvent) => { + if (!draggedElement) return; + + const touch = e.changedTouches[0]; + const element = document.elementFromPoint(touch.clientX, touch.clientY); + + // Check if dropped on sentence building area + const sentenceArea = element?.closest('[data-drop-zone="sentence"]'); + if (sentenceArea && draggedElement.type === 'available') { + // Only add if the word is still in available words + if (availableWords[draggedElement.index] === draggedElement.word) { + addWordToSentence(draggedElement.word, draggedElement.index); + } + } + + // Check if dropped on another word + const wordElement = element?.closest('[data-word-index]'); + if (wordElement && draggedElement.type === 'available') { + const targetIndex = parseInt(wordElement.getAttribute('data-word-index') || '0'); + if (targetIndex !== undefined && availableWords[draggedElement.index] === draggedElement.word) { + const newArrangedWords = [...arrangedWords]; + const removedWord = newArrangedWords[targetIndex]; + newArrangedWords[targetIndex] = draggedElement.word; + setArrangedWords(newArrangedWords); + + if (removedWord) { + setAvailableWords([...availableWords, removedWord]); + } + + setAvailableWords(availableWords.filter((_, i) => i !== draggedElement.index)); + } + } + + setDraggedElement(null); + setTouchStartPos(null); + }; + + const checkAnswer = async () => { + const correct = JSON.stringify(arrangedWords) === JSON.stringify(currentQuestion.correct); + setIsCorrect(correct); + setShowFeedback(true); + + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `sentencebuilder_${currentLevel}_${currentQuestionIndex}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'sentenceBuilder', + arrangedWords.join(' '), + currentQuestion.correct.join(' '), + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'sentenceBuilder', + userAnswer: arrangedWords.join(' '), + correctAnswer: currentQuestion.correct.join(' '), + isCorrect: correct, + responseTime: responseTime, + complexity: currentQuestion.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(score + 1); + setTotalCorrect(totalCorrect + 1); + } + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setShowFeedback(false); + } else { + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'Sentence Builder Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + console.log(`Sentence game completed for ${selectedLanguage}, previous level: ${previousLevel}`); + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const handleBackClick = async () => { + await sessionTelemetryManager.endSubSessionWithBackButton(); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setArrangedWords([]); + setAvailableWords([]); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + // Reset session cache for completely fresh questions + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + // Start new session and regenerate questions + if (selectedLanguage) { + const session = startSession(gameKey); + const newQuestions = generateSentenceQuestions( + selectedLanguage, + currentLevel, + difficultySettings.complexity, + 10, + new Set() + ); + setQuestions(newQuestions); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/sentence-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/sentence-game'); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Sentence Master - Perfect Grammar!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Word Wizard - Great Building!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level selector is requested (after preview) + if (showLevelSelector) { + const levelSelectorCurrentLevel = gameProgress.currentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} // Go back to preview instead of main menu + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="Sentence Builder" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + { + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + console.log(`Manual advancement: ${currentLevel} -> ${nextLevel} for ${selectedLanguage}`); + manuallyAdvanceLevel(gameKey, nextLevel); + navigate(`/sentence-game/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ Sentence Builder +

+
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ +
+
+
+
+ ); +} + +// Helper function to map complexity to sentence difficulty +function mapComplexityToSentenceLevel(complexity: string): string { + switch (complexity.toLowerCase()) { + case 'easy': + case 'beginner': + return 'basic'; + case 'medium': + return 'intermediate'; + case 'hard': + return 'advanced'; + case 'expert': + return 'expert'; + case 'master': + return 'master'; + default: + return 'basic'; + } +} + +// Generate sentence questions from JSON data +function generateSentenceQuestions( + language: Language, + level: number, + complexity: string, + count: number = 10, + usedQuestions: Set = new Set() +): SentenceQuestion[] { + + // Load sentence data from JSON + // Ensure only supported languages are passed (sentenceDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (language === 'en' || language === 'te' || language === 'mr' || language === 'kn') + ? language + : 'en'; + const sentenceData = loadSentenceData(supportedLanguage, complexity); + + if (sentenceData.length === 0) { + console.warn(`No sentence data found for ${language} ${complexity}`); + return []; + } + + const questions: SentenceQuestion[] = []; + const localUsedSentences: string[] = []; + + // Filter out sentences already used in this session + const availableSentences = sentenceData.filter(sentence => + !usedQuestions.has(sentence.correct.join(' ')) + ); + + // Reset session cache if we've exhausted most sentences + let workingSentences = [...availableSentences]; + if (workingSentences.length < count) { + console.log(`🔄 Resetting sentence cache for ${language} ${complexity} - only ${workingSentences.length} fresh sentences remaining`); + sentenceData.forEach(sentence => usedQuestions.delete(sentence.correct.join(' '))); + workingSentences = [...sentenceData]; // Create fresh array instead of pushing to filtered array + } + + const actualCount = Math.min(count, workingSentences.length); + console.log(`🎮 Generating ${actualCount} UNIQUE child-friendly sentence questions for ${language} level ${level} (${complexity})`); + + for (let i = 0; i < actualCount; i++) { + // Filter out sentences already used in this question set + const unusedSentences = workingSentences.filter(sentence => + !localUsedSentences.includes(sentence.correct.join(' ')) + ); + + if (unusedSentences.length === 0) { + console.warn(`⚠️ No more sentence options available for ${language} ${complexity}`); + break; + } + + // Pick a random sentence from unused sentences + const randomIndex = Math.floor(Math.random() * unusedSentences.length); + const sentence = unusedSentences[randomIndex]; + + // Track usage + const sentenceKey = sentence.correct.join(' '); + localUsedSentences.push(sentenceKey); + usedQuestions.add(sentenceKey); + + // Create properly shuffled words for the challenge (different from correct order) + const shuffledWords = [...sentence.words].sort(() => Math.random() - 0.5); + + questions.push({ + words: shuffledWords, + correct: [...sentence.correct], + language, + complexity, + level + }); + } + + // Shuffle questions to randomize order + return questions.sort(() => Math.random() - 0.5); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/SentenceGameCore.tsx b/src/lib/axl-explorations/src/components/games/SentenceGameCore.tsx new file mode 100644 index 00000000..98f0f0e3 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/SentenceGameCore.tsx @@ -0,0 +1,369 @@ +import { useState, useEffect, useRef } from "react"; +import { Button } from "../ui/button"; +import { Volume2, CheckCircle } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { playAudio, playTTS } from "../../utils/audioUtils"; +import { ContinueButton } from "./ContinueButton"; + +export interface SentenceQuestion { + words: string[]; + correct: string[]; + language: Language; + complexity: string; + level: number; +} + +export interface SentenceGameCoreProps { + currentQuestion: SentenceQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + arrangedWords: string[]; + availableWords: string[]; + showFeedback?: boolean; + isCorrect?: boolean; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onWordClick: (word: string, index: number) => void; + onRemoveWord: (index: number) => void; + onCheckAnswer?: () => void; + onContinue?: () => void; + className?: string; + feedbackLanguageOverride?: Language; +} + +export function SentenceGameCore({ + currentQuestion, + mode, + selectedLanguage, + arrangedWords, + availableWords, + showFeedback = false, + isCorrect = false, + isPreview = false, + demoStep = '', + showHandPointer = false, + disabled = false, + onWordClick, + onRemoveWord, + onCheckAnswer, + onContinue, + className = '', + feedbackLanguageOverride, +}: SentenceGameCoreProps) { + const [currentAudio, setCurrentAudio] = useState(null); + const [draggedElement, setDraggedElement] = useState<{word: string, index: number, type: 'available' | 'arranged'} | null>(null); + const [dragOverIndex, setDragOverIndex] = useState(null); + const optionsRef = useRef(null); + + const getLocalizedText = (key: string) => { + const texts = { + listenToSentence: { + en: 'Listen to the sentence', + te: 'వాక్యాన్ని వినండి', + kn: 'ವಾಕ್ಯವನ್ನು ಕೇಳಿ', + mr: 'वाक्य ऐका' + }, + buildSentence: { + en: 'Build your sentence!', + te: 'మీ వాక్యాన్ని నిర్మించండి!', + kn: 'ನಿಮ್ಮ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿ!', + mr: 'तुमचे वाक्य तयार करा!' + }, + dragWordsHere: { + en: 'Drag words here...', + te: 'పదాలను ఇక్కడ లాగండి...', + kn: 'ಪದಗಳನ್ನು ಇಲ್ಲಿ ಎಳೆಯಿರಿ...', + mr: 'शब्द येथे ड्रॅग करा...' + }, + availableWords: { + en: 'Available words:', + te: 'అందుబాటులో ఉన్న పదాలు:', + kn: 'ಲಭ್ಯವಿರುವ ಪದಗಳು:', + mr: 'उपलब्ध शब्द:' + }, + checkMySentence: { + en: 'Check My Sentence', + te: 'నా వాక్యాన్ని తనిఖీ చేయండి', + kn: 'ನನ್ನ ವಾಕ್ಯವನ್ನು ಪರಿಶೀಲಿಸಿ', + mr: 'माझे वाक्य तपासा' + }, + successMessage: { + en: '🎉 Correct!', + te: '🎉 సరైనది!', + kn: '🎉 ಸರಿ!', + mr: '🎉 बरोबर!' + }, + failureMessage: { + en: '😢 Oops! Wrong!', + te: '😢 అయ్యో! తప్పు!', + kn: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + mr: '😢 अरेच्या! चुकीचे!' + } + }; + const language = selectedLanguage; + return texts[key as keyof typeof texts]?.[language] || texts[key as keyof typeof texts]?.en || ''; + }; + + // Enhanced audio function with proper language-specific voice selection + const playAudio = async (text: string, language: Language) => { + // Cancel any existing speech + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + + // Pause any existing audio + if (currentAudio) { + currentAudio.pause(); + currentAudio.currentTime = 0; + } + + setTimeout(() => { + const utterance = new SpeechSynthesisUtterance(text); + + // Set language-specific voice + const voices = speechSynthesis.getVoices(); + let selectedVoice = null; + + switch (language) { + case 'te': + utterance.lang = 'te-IN'; + utterance.rate = 0.9; + utterance.pitch = 1.0; + utterance.volume = 1.0; + selectedVoice = voices.find(voice => voice.lang.includes('te-IN') || voice.lang.includes('te')); + break; + case 'kn': + utterance.lang = 'kn-IN'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + selectedVoice = voices.find(voice => voice.lang.includes('kn-IN') || voice.lang.includes('kn')); + break; + case 'mr': + utterance.lang = 'mr-IN'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + selectedVoice = voices.find(voice => voice.lang.includes('mr-IN') || voice.lang.includes('mr')); + break; + default: + utterance.lang = 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + selectedVoice = voices.find(voice => voice.lang.includes('en-US') || voice.lang.includes('en')); + break; + } + + if (selectedVoice) { + utterance.voice = selectedVoice; + } + + speechSynthesis.speak(utterance); + }, 50); + }; + + const handlePlayAudio = async () => { + if (currentQuestion.correct && currentQuestion.correct.length > 0) { + await playAudio(currentQuestion.correct.join(' '), feedbackLanguageOverride || selectedLanguage); + } + }; + + // Drag and Drop Handlers + const handleDragStart = (e: React.DragEvent, word: string, index: number, type: 'available' | 'arranged') => { + if (disabled || showFeedback) return; + + setDraggedElement({ word, index, type }); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', word); + + // Add visual feedback + if (e.currentTarget instanceof HTMLElement) { + e.currentTarget.style.opacity = '0.5'; + } + }; + + const handleDragEnd = (e: React.DragEvent) => { + if (e.currentTarget instanceof HTMLElement) { + e.currentTarget.style.opacity = '1'; + } + setDraggedElement(null); + setDragOverIndex(null); + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleDrop = (e: React.DragEvent, dropType: 'sentence' | 'word', dropIndex?: number) => { + e.preventDefault(); + + if (!draggedElement || disabled || showFeedback) return; + + if (dropType === 'sentence') { + // Drop on sentence building area + if (draggedElement.type === 'available') { + onWordClick(draggedElement.word, draggedElement.index); + } + } else if (dropType === 'word' && dropIndex !== undefined) { + // Drop on existing word in sentence + if (draggedElement.type === 'available') { + // Replace the word at dropIndex + onWordClick(draggedElement.word, draggedElement.index); + // Remove the word that was replaced + onRemoveWord(dropIndex); + } else if (draggedElement.type === 'arranged') { + // Reorder words within sentence + const newArrangedWords = [...arrangedWords]; + const draggedWord = newArrangedWords[draggedElement.index]; + newArrangedWords.splice(draggedElement.index, 1); + newArrangedWords.splice(dropIndex, 0, draggedWord); + + // Update the arranged words (this would need to be handled by parent component) + // For now, we'll use the existing click handlers + } + } + + setDraggedElement(null); + setDragOverIndex(null); + }; + + const handleDragEnter = (e: React.DragEvent, index?: number) => { + e.preventDefault(); + if (index !== undefined) { + setDragOverIndex(index); + } + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + setDragOverIndex(null); + }; + + return ( +
+ {/* Game Icon */} +
+
+ 📝 +
+
+ + + {/* Sentence Building Area */} +
+
handleDrop(e, 'sentence')} + data-drop-zone="sentence" + > + {arrangedWords.length === 0 ? ( +

{getLocalizedText('dragWordsHere')}

+ ) : ( + arrangedWords.map((word, index) => ( + + )) + )} +
+
+ + {/* Available Words */} +
0 ? "mb-4 sm:mb-6" : "mb-3"}> + {!showFeedback &&

{getLocalizedText('availableWords')}

} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+ {availableWords.map((word, index) => ( + + ))} +
+
+
+ + {/* Check Answer Button and Feedback Area */} +
+ {arrangedWords.length === currentQuestion.correct.length && !showFeedback && onCheckAnswer && ( + + )} + + {/* Feedback */} + {showFeedback && ( + <> + {isCorrect ? ( +
+

+ {getLocalizedText('successMessage')} +

+
+ ) : ( +
+

{getLocalizedText('failureMessage')}

+
+ )} + + {/* Continue Button */} + + + )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/SentenceGamePreview.tsx b/src/lib/axl-explorations/src/components/games/SentenceGamePreview.tsx new file mode 100644 index 00000000..13dbeb37 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/SentenceGamePreview.tsx @@ -0,0 +1,621 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Volume2, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { SentenceGameCore, type SentenceQuestion } from "./SentenceGameCore"; + +interface SentenceGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" + | 'showWords' // Show the scrambled words after ready click + | 'instruction2' // After showing words, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show word options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "Sentence Builder", + description: "Arrange scrambled words to make a complete sentence!", + steps: [ + "📝 Look at the scrambled words", + "🖱️ Click words in correct order", + "📋 Build your sentence step by step", + "✅ Complete the sentence!" + ], + instruction1: "Click 'I'm Ready' to see the scrambled words", + instruction2: "Good! Now look at these scrambled words", + instruction3: "Now click the words in the correct order to build a sentence", + instruction4: "Perfect! You built a complete sentence!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Click I'm Ready to see the scrambled words", + narration2: "Good! Now look at these scrambled words", + narration3: "Now click the words in the correct order to build a sentence", + narration4: "Perfect! You built a complete sentence!", + howToPlay: "How to Play", + buildSentence: "Build your sentence!", + demo: { + scrambledWords: ["is", "good", "Reading"], + correctOrder: ["Reading", "is", "good"], + correctSentence: "Reading is good", + explanation: "Perfect! You arranged the words correctly!" + } + }, + te: { + title: "వాక్య నిర్మాణం", + description: "గందరగోళ పదాలను అమర్చి పూర్తి వాక్యాన్ని తయారు చేయండి!", + steps: [ + "📝 గందరగోళ పదాలను చూడండి", + "🖱️ సరైన క్రమంలో పదాలను క్లిక్ చేయండి", + "📋 దశలవారీగా మీ వాక్యాన్ని నిర్మించండి", + "✅ వాక్యాన్ని పూర్తి చేయండి!" + ], + instruction1: "గందరగోళ పదాలను చూడటానికి 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ గందరగోళ పదాలను చూడండి", + instruction3: "ఇప్పుడు వాక్యాన్ని నిర్మించడానికి సరైన క్రమంలో పదాలను క్లిక్ చేయండి", + instruction4: "పర్ఫెక్ట్! మీరు పూర్తి వాక్యాన్ని నిర్మించారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "గందరగోళ పదాలను చూడటానికి నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ గందరగోళ పదాలను చూడండి", + narration3: "ఇప్పుడు వాక్యాన్ని నిర్మించడానికి సరైన క్రమంలో పదాలను క్లిక్ చేయండి", + narration4: "పర్ఫెక్ట్! మీరు పూర్తి వాక్యాన్ని నిర్మించారు!", + howToPlay: "ఎలా ఆడాలి", + buildSentence: "మీ వాక్యాన్ని నిర్మించండి!", + demo: { + scrambledWords: ["మంచిది", "చదవడం", "చాలా"], + correctOrder: ["చదవడం", "చాలా", "మంచిది"], + correctSentence: "చదవడం చాలా మంచిది", + explanation: "పరిపూర్ణం! మీరు పదాలను సరిగ్గా అమర్చారు!" + } + }, + kn: { + title: "ವಾಕ್ಯ ನಿರ್ಮಾಣ", + description: "ಗೊಂದಲದ ಪದಗಳನ್ನು ಜೋಡಿಸಿ ಸಂಪೂರ್ಣ ವಾಕ್ಯವನ್ನು ಮಾಡಿ!", + steps: [ + "📝 ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಿ", + "🖱️ ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಪದಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + "📋 ಹಂತ ಹಂತವಾಗಿ ನಿಮ್ಮ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿ", + "✅ ವಾಕ್ಯವನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ!" + ], + instruction1: "ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಲು 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಿ", + instruction3: "ಈಗ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಲು ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಪದಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction4: "ಪರಿಪೂರ್ಣ! ನೀವು ಸಂಪೂರ್ಣ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಲು ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಗೊಂದಲದ ಪದಗಳನ್ನು ನೋಡಿ", + narration3: "ಈಗ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಲು ಸರಿಯಾದ ಕ್ರಮದಲ್ಲಿ ಪದಗಳನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ", + narration4: "ಪರಿಪೂರ್ಣ! ನೀವು ಸಂಪೂರ್ಣ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + buildSentence: "ನಿಮ್ಮ ವಾಕ್ಯವನ್ನು ನಿರ್ಮಿಸಿ!", + demo: { + scrambledWords: ["ಒಳ್ಳೆಯದು", "ಓದುವುದು", "ತುಂಬಾ"], + correctOrder: ["ಓದುವುದು", "ತುಂಬಾ", "ಒಳ್ಳೆಯದು"], + correctSentence: "ಓದುವುದು ತುಂಬಾ ಒಳ್ಳೆಯದು", + explanation: "ಪರಿಪೂರ್ಣ! ನೀವು ಪದಗಳನ್ನು ಸರಿಯಾಗಿ ಜೋಡಿಸಿದ್ದೀರಿ!" + } + }, + mr: { + title: "वाक्य बांधकाम", + description: "गोंधळलेले शब्द मांडून पूर्ण वाक्य तयार करा!", + steps: [ + "📝 गोंधळलेले शब्द पहा", + "🖱️ योग्य क्रमाने शब्दांवर क्लिक करा", + "📋 टप्प्याटप्प्याने तुमचे वाक्य तयार करा", + "✅ वाक्य पूर्ण करा!" + ], + instruction1: "गोंधळलेले शब्द पाहण्यासाठी 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता ही गोंधळलेली शब्दे पहा", + instruction3: "आता वाक्य तयार करण्यासाठी योग्य क्रमाने शब्दांवर क्लिक करा", + instruction4: "उत्कृष्ट! तुम्ही पूर्ण वाक्य तयार केले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "गोंधळलेले शब्द पाहण्यासाठी मी तयार आहे क्लिक करा", + narration2: "चांगले! आता ही गोंधळलेली शब्दे पहा", + narration3: "आता वाक्य तयार करण्यासाठी योग्य क्रमाने शब्दांवर क्लिक करा", + narration4: "उत्कृष्ट! तुम्ही पूर्ण वाक्य तयार केले!", + howToPlay: "कसे खेळायचे", + buildSentence: "तुमचे वाक्य तयार करा!", + demo: { + scrambledWords: ["चांगले", "वाचन", "खूप"], + correctOrder: ["वाचन", "खूप", "चांगले"], + correctSentence: "वाचन खूप चांगले", + explanation: "परिपूर्ण! तुम्ही शब्दांची योग्य मांडणी केली!" + } + } +}; + +export function SentenceGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: SentenceGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedWords, setSelectedWords] = useState([]); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showWords, setShowWords] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question object for SentenceGameCore + const demoQuestion: SentenceQuestion = { + words: instructions.demo.scrambledWords, + correct: instructions.demo.correctOrder, + language: contentLanguage, + complexity: 'basic', + level: 1 + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using combined sentence games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined sentence games audio files for Sentence Builder + const gameName = 'Combined Sentence Games'; + const subGame = 'Sentence Builder'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + // Play word sound + const playWordSound = (text: string) => { + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = audioLanguage === 'te' ? 'te-IN' : + audioLanguage === 'kn' ? 'kn-IN' : + audioLanguage === 'mr' ? 'mr-IN' : + audioLanguage === 'hi' ? 'hi-IN' : 'en-US'; + utterance.rate = 1.0; + utterance.pitch = 1.0; + if (!isAudioStopped()) { + speechSynthesis.speak(utterance); + } + }; + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + setCurrentStep(0); + break; + case 'showWords': + case 'instruction2': + setCurrentStep(1); + break; + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(2); + break; + case 'instruction4': + case 'complete': + setCurrentStep(3); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setSelectedWords([]); + setShowFeedback(false); + setShowWords(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle ready button click + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + + // Show words + setShowWords(true); + setDemoStep('showWords'); + + // Wait a moment then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle word click + const handleWordClick = async (word: string) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + const newSelectedWords = [...selectedWords, word]; + setSelectedWords(newSelectedWords); + + // Check if sentence is complete + if (newSelectedWords.length === instructions.demo.correctOrder.length) { + setShowFeedback(true); + + const isCorrect = JSON.stringify(newSelectedWords) === JSON.stringify(instructions.demo.correctOrder); + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedWords([]); + }, 2000); + } + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedWords([]); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowWords(false); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showWordsDisplay = showWords && (demoStep === 'showWords' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'); + const showWordOptions = demoStep === 'waitForAnswer' || demoStep === 'instruction4' || demoStep === 'complete'; + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler with audio cleanup + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Start game handler with audio cleanup + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
+ {/* Fixed Layout Structure */} +
+ {/* Ready Button - Show initially */} + {(demoStep === 'instruction1' || demoStep === 'waitForReady') && !showWordsDisplay && ( +
+
+ {contentLanguage === 'en' ? "I'm Ready" : contentLanguage === 'te' ? 'నేను సిద్ధంగా ఉన్నాను' : contentLanguage === 'kn' ? 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' : 'मी तयार आहे'} +
+ {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {/* Words Display - Show after ready click */} + {(demoStep === 'showWords' || demoStep === 'instruction2' || demoStep === 'instruction3') && !showWordOptions && ( +
+
+
+ {instructions.demo.scrambledWords.map((word, index) => ( + + {word} + + ))} +
+
+
+ )} + + {/* Sentence Building Area - Show after instruction 3 */} + {showWordOptions && ( + !selectedWords.includes(word))} + showFeedback={showFeedback} + isCorrect={isCorrectAnswer} + isPreview={true} + demoStep={demoStep} + showHandPointer={demoStep === 'waitForAnswer' && !showFeedback} + disabled={demoStep !== 'waitForAnswer'} + onWordClick={(word, index) => { + const actualIndex = instructions.demo.scrambledWords.findIndex(w => w === word); + if (actualIndex !== -1) { + handleWordClick(word); + } + }} + onRemoveWord={(index) => { + const newSelectedWords = selectedWords.filter((_, i) => i !== index); + setSelectedWords(newSelectedWords); + }} + feedbackLanguageOverride={audioLanguage} + /> + )} +
+
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} diff --git a/src/lib/axl-explorations/src/components/games/SyllableGame.tsx b/src/lib/axl-explorations/src/components/games/SyllableGame.tsx new file mode 100644 index 00000000..f4c604db --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/SyllableGame.tsx @@ -0,0 +1,299 @@ +import { useState, useEffect } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { AudioButton } from "../AudioButton"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { ArrowLeft, RotateCcw, CheckCircle } from "lucide-react"; +import { cn } from "../../lib/utils"; + +const SAMPLE_SYLLABLES = [ + { + phonemes: ["क", "ना"], + target: "कना", + sounds: ["ka", "na"], + audio: "Drag and drop to form the syllable: ka + na" + }, + { + phonemes: ["म", "ता"], + target: "मता", + sounds: ["ma", "ta"], + audio: "Drag and drop to form the syllable: ma + ta" + }, + { + phonemes: ["स", "रा"], + target: "सरा", + sounds: ["sa", "ra"], + audio: "Drag and drop to form the syllable: sa + ra" + }, +]; + +interface SyllableGameProps { + onBack: () => void; +} + +export function SyllableGame({ onBack }: SyllableGameProps) { + const [currentLevel, setCurrentLevel] = useState(0); + const [score, setScore] = useState(0); + const [arrangedPhonemes, setArrangedPhonemes] = useState([]); + const [availablePhonemes, setAvailablePhonemes] = useState([]); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + + const currentQuestion = SAMPLE_SYLLABLES[currentLevel]; + + useEffect(() => { + // Shuffle phonemes initially + const shuffled = [...currentQuestion.phonemes].sort(() => Math.random() - 0.5); + setAvailablePhonemes(shuffled); + setArrangedPhonemes([]); + }, [currentLevel]); + + const addPhonemeToSyllable = (phoneme: string, index: number) => { + setArrangedPhonemes([...arrangedPhonemes, phoneme]); + setAvailablePhonemes(availablePhonemes.filter((_, i) => i !== index)); + }; + + const removePhonemeFromSyllable = (index: number) => { + const phoneme = arrangedPhonemes[index]; + setAvailablePhonemes([...availablePhonemes, phoneme]); + setArrangedPhonemes(arrangedPhonemes.filter((_, i) => i !== index)); + }; + + const checkAnswer = () => { + const formedSyllable = arrangedPhonemes.join(''); + const correct = formedSyllable === currentQuestion.target; + setIsCorrect(correct); + setShowFeedback(true); + + if (correct) { + setScore(score + 25); + setTotalCorrect(totalCorrect + 1); + } + + setTimeout(() => { + if (correct && currentLevel < SAMPLE_SYLLABLES.length - 1) { + setCurrentLevel(currentLevel + 1); + const nextQuestion = SAMPLE_SYLLABLES[currentLevel + 1]; + const shuffled = [...nextQuestion.phonemes].sort(() => Math.random() - 0.5); + setAvailablePhonemes(shuffled); + setArrangedPhonemes([]); + setShowFeedback(false); + } else if (correct || currentLevel === SAMPLE_SYLLABLES.length - 1) { + // Game complete - show success screen + setIsGameComplete(true); + } else { + setShowFeedback(false); + } + }, 2000); + }; + + const resetGame = () => { + setCurrentLevel(0); + setScore(0); + setTotalCorrect(0); + const shuffled = [...SAMPLE_SYLLABLES[0].phonemes].sort(() => Math.random() - 0.5); + setAvailablePhonemes(shuffled); + setArrangedPhonemes([]); + setShowFeedback(false); + setIsGameComplete(false); + }; + + const calculateStars = () => { + const percentage = (totalCorrect / SAMPLE_SYLLABLES.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (totalCorrect === SAMPLE_SYLLABLES.length) { + achievements.push("Syllable Wizard - Perfect Building!"); + } + if (totalCorrect >= 2) { + achievements.push("Magic Builder - Great Combining!"); + } + return achievements; + }; + + // Show success screen when game is complete + if (isGameComplete) { + return ( + { + resetGame(); + }} + /> + ); + } + + return ( +
+
+ {/* Header */} +
+ + +

+ Syllable Magic +

+ + {/* */} +
+ + {/* Progress */} + + + + + {/* Game Area */} + +
+

+ Build the Syllable! +

+ + {/* Visual prompt showing the phonemes */} +
+ {currentQuestion.phonemes.map((phoneme, index) => ( +
+
+ {phoneme} +
+ {index < currentQuestion.phonemes.length - 1 && ( + + + )} +
+ ))} +
+ + {/* Audio for each phoneme */} +
+ {currentQuestion.sounds.map((sound, index) => ( + + ))} +
+ + +

+ Target: {currentQuestion.target} +

+
+ + {/* Syllable Building Area */} +
+

Build your syllable:

+
+ {arrangedPhonemes.length === 0 ? ( +

Drag phonemes here...

+ ) : ( +
+ {arrangedPhonemes.map((phoneme, index) => ( + + ))} +
+ )} +
+
+ + {/* Available Phonemes */} +
+

Available phonemes:

+
+ {availablePhonemes.map((phoneme, index) => ( + + ))} +
+
+ + {/* Check Answer Button */} + {arrangedPhonemes.length === currentQuestion.phonemes.length && !showFeedback && ( +
+ +
+ )} + + {/* Feedback */} + {showFeedback && ( +
+ {isCorrect ? ( +
+

Magical! ✨

+

You built "{arrangedPhonemes.join('')}" perfectly!

+
+ ) : ( +
+

Almost there! 🪄

+

Try building: "{currentQuestion.target}"

+
+ )} +
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/games/TrueFalseGame.tsx b/src/lib/axl-explorations/src/components/games/TrueFalseGame.tsx new file mode 100644 index 00000000..1a5c7419 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/TrueFalseGame.tsx @@ -0,0 +1,636 @@ +import { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { ProgressBar } from "../ProgressBar"; +import { SuccessScreen } from "../SuccessScreen"; +import { LevelSelector } from "../LevelSelector"; +import { TryAgain } from "../TryAgain"; +import { ArrowLeft, RotateCcw, TrendingUp, Globe, ArrowUp, ArrowDown, ArrowRight, Check, X } from "lucide-react"; +import { sessionManager } from "../../utils/sessionManager"; +import { sessionTelemetryManager } from "../../utils/sessionTelemetryManager"; +import { trackingAssessmentService, QuestionSummary } from "../../utils/trackingAssessmentService"; +import { useLearningProgress } from "../../hooks/useLearningProgress"; +import { loadTrueFalseQuestions, TrueFalseQuestion, Language as TrueFalseDataLanguage, DifficultyLevel } from "../../utils/trueFalseDataLoader"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { Language, getNativeLanguageName } from "../../constants/languages"; +import TrueFalseGamePreview from "./TrueFalseGamePreview"; +import { TrueFalseGameCore } from "./TrueFalseGameCore"; + + +interface TrueFalseGameProps { + onBack: () => void; +} + +export function TrueFalseGame({ onBack }: TrueFalseGameProps) { + const navigate = useNavigate(); + const { level } = useParams<{ level?: string }>(); + + const { + startSession, + recordAnswer, + endSession, + getGameProgress, + getDifficultySettings, + manuallyAdvanceLevel, + currentSession + } = useLearningProgress(); + + const { selectedLanguage } = useLanguage(); + + // Determine if we're showing level selector or playing a specific level + const isLevelSelector = !level || level === 'select'; + const selectedLevel = level && level !== 'select' ? parseInt(level) : null; + const showLevelSelector = isLevelSelector; + const [showPreview, setShowPreview] = useState(true); + const [forcePreview, setForcePreview] = useState(false); + const [backendCurrentLevel, setBackendCurrentLevel] = useState(1); + const [isLoadingLevel, setIsLoadingLevel] = useState(true); + const [level1HasProgress, setLevel1HasProgress] = useState(false); // Track if level 1 has any percentage > 0% + const [questions, setQuestions] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [isGameComplete, setIsGameComplete] = useState(false); + const [totalCorrect, setTotalCorrect] = useState(0); + const [showLevelUp, setShowLevelUp] = useState(false); + const [previousLevel, setPreviousLevel] = useState(1); + const [levelFailed, setLevelFailed] = useState(false); + + + const [usedQuestions, setUsedQuestions] = useState>(new Set()); + + // Telemetry state + const [questionStartTime, setQuestionStartTime] = useState(0); + + // Tracking Assessment state + const [levelStartTime, setLevelStartTime] = useState(0); + const [questionSummaries, setQuestionSummaries] = useState([]); + + // Language-specific level configurations (using English names for all languages) + const getLanguageLevels = (language: Language) => { + switch (language) { + case 'te': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + case 'mr': + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + default: + return { + maxLevels: 10, + levelNames: ['Beginner', 'Easy', 'Medium', 'Hard', 'Expert'] + }; + } + }; + + const gameKey = selectedLanguage ? `trueFalse_${selectedLanguage}` : 'trueFalse'; + const gameProgress = getGameProgress(gameKey); + const currentLevel = selectedLevel || gameProgress.currentLevel; + const difficultySettings = getDifficultySettings(gameKey, currentLevel); + const languageLevels = getLanguageLevels(selectedLanguage || 'en'); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + setShowPreview(false); // Hide preview when level is selected + setIsGameComplete(false); + setScore(0); + setTotalCorrect(0); + setCurrentQuestionIndex(0); + setSelectedAnswer(null); + setShowFeedback(false); + setLevelFailed(false); + } + }, [selectedLevel, selectedLanguage]); + + // Initialize game session and questions + useEffect(() => { + const initializeGame = async () => { + if (selectedLanguage && selectedLevel !== null && !isGameComplete) { + await new Promise(resolve => setTimeout(resolve, 100)); + + const session = startSession(gameKey); + setPreviousLevel(currentLevel); + + // Initialize tracking assessment + const now = Date.now(); + setLevelStartTime(now); + setQuestionStartTime(now); + setQuestionSummaries([]); + + // Telemetry subsession + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + if (currentSubSession && currentSubSession.isActive) { + await sessionTelemetryManager.endSubSession(); + } + await sessionTelemetryManager.startSubSession(gameKey, currentLevel, selectedLanguage); + + // Ensure only supported languages are passed (trueFalseDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const newQuestions = loadTrueFalseQuestions( + supportedLanguage, + difficultySettings.complexity as DifficultyLevel, + 10, + usedQuestions + ); + setQuestions(newQuestions); + } + }; + initializeGame(); + }, [selectedLanguage, selectedLevel, gameKey, isGameComplete]); + + // Fetch backend current level on mount + useEffect(() => { + const fetchBackendLevel = async () => { + if (!selectedLanguage) return; + + const currentUser = sessionManager.getCurrentUser(); + if (!currentUser) { + setIsLoadingLevel(false); + return; + } + + try { + setIsLoadingLevel(true); + + // Extract game name without language suffix + const gameName = gameKey.split('_')[0]; + + // Search for level stats using current user + const searchParams = { + userId: currentUser.username, + courseId: gameName, + unitId: selectedLanguage + }; + + const result = await trackingAssessmentService.searchAssessmentTracking(searchParams); + + // Handle the enhanced backend response format + if (result.success && result.data && typeof result.data === 'object') { + // Check if level 1 has any progress (> 0%) + const level1Data = (result.data as any)['level1']; + const level1Percent = level1Data?.metadata?.scorePercentage ?? 0; + const level1Completed = level1Data?.metadata?.isCompleted ?? false; + const hasLevel1Progress = level1Completed || level1Percent > 0; + setLevel1HasProgress(hasLevel1Progress); + + // Compute effective current level from progress (score > 0 or completed) + let highestSuccessfulLevel = 0; + Object.keys(result.data).forEach((levelKey) => { + if (!levelKey.startsWith('level')) return; + const levelNumber = parseInt(levelKey.replace('level', '')); + if (Number.isNaN(levelNumber)) return; + const levelData = (result.data as any)[levelKey]; + const percent = levelData?.metadata?.scorePercentage ?? 0; + const completed = levelData?.metadata?.isCompleted ?? false; + if (completed || percent > 0) { + highestSuccessfulLevel = Math.max(highestSuccessfulLevel, levelNumber); + } + }); + + const computedFromProgress = Math.min( + Math.max(1, (highestSuccessfulLevel > 0 ? highestSuccessfulLevel + 1 : 1)), + languageLevels.maxLevels + ); + const backendProvided = result.metadata?.currentLevel || 1; + const effectiveCurrentLevel = Math.min( + Math.max(computedFromProgress, backendProvided), + languageLevels.maxLevels + ); + setBackendCurrentLevel(effectiveCurrentLevel); + } + } catch (error) { + console.error('Error fetching backend level:', error); + } finally { + setIsLoadingLevel(false); + } + }; + + fetchBackendLevel(); + }, [selectedLanguage, gameKey]); + + // Note: Page refresh is handled in App.tsx via beforeunload event + // The initializeGame useEffect above will automatically start a new subsession after refresh + + // Auto-show level selector when component loads + useEffect(() => { + if (selectedLanguage && selectedLevel === null) { + // Level selector is now controlled by URL routing + // No need to set showLevelSelector state + } + }, [selectedLanguage, selectedLevel]); + + // Reset game state when navigating to a new level via URL + useEffect(() => { + if (selectedLevel !== null) { + // Reset game state when navigating to a specific level + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + } + }, [selectedLevel, selectedLanguage]); + + const currentQuestion = questions[currentQuestionIndex]; + + // Track question start time + useEffect(() => { + if (currentQuestion) setQuestionStartTime(Date.now()); + }, [currentQuestionIndex]); + + + + // Get localized question text - just the statement without asking true/false + const getQuestionText = (statement: string, language: Language): string => { + return statement; // Just return the statement without adding true/false question + }; + + + + // Handle keyboard input for arrow keys + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + if (showFeedback || isGameComplete || !currentQuestion) return; + + if (event.key === 'ArrowRight') { + handleAnswerSelect(true); + } else if (event.key === 'ArrowLeft') { + handleAnswerSelect(false); + } + }; + + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + }, [showFeedback, isGameComplete, currentQuestion]); + + const handleAnswerSelect = async (isTrue: boolean) => { + if (showFeedback) return; + + setSelectedAnswer(isTrue); + const correct = isTrue === currentQuestion.isTrue; + setIsCorrect(correct); + setShowFeedback(true); + + recordAnswer(correct); + + // Telemetry assess + const responseTime = questionStartTime > 0 ? Date.now() - questionStartTime : 0; + const questionId = `truefalse_${currentLevel}_${currentQuestionIndex}`; + await sessionTelemetryManager.sendAssessEvent( + questionId, + 'trueFalse', + isTrue, + currentQuestion.isTrue, + correct, + responseTime + ); + sessionTelemetryManager.updateSubSession(correct); + + // Store question summary for tracking assessment + const questionSummary: QuestionSummary = { + questionId: questionId, + questionType: 'trueFalse', + userAnswer: String(isTrue), + correctAnswer: String(currentQuestion.isTrue), + isCorrect: correct, + responseTime: responseTime, + complexity: difficultySettings.complexity + }; + setQuestionSummaries(prev => [...prev, questionSummary]); + + if (correct) { + setScore(prevScore => prevScore + 1); + setTotalCorrect(prevTotal => prevTotal + 1); + } + }; + + const handleContinue = useCallback(async () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + setSelectedAnswer(null); + setShowFeedback(false); + } else { + const finalCorrect = totalCorrect + (isCorrect ? 1 : 0); + const scorePercentage = (finalCorrect / questions.length) * 100; + const canAdvance = true; + + // Calculate total time spent + const totalTimeSpent = Math.floor((Date.now() - levelStartTime) / 1000); + + // Send tracking assessment data (for both pass and fail attempts) + const currentUser = sessionManager.getCurrentUser(); + if (currentUser) { + const currentSession = sessionTelemetryManager.getCurrentSession(); + const currentSubSession = sessionTelemetryManager.getCurrentSubSession(); + const sessionId = currentSession?.sessionId; + const subsessionId = currentSubSession?.subSessionId; + + setQuestionSummaries((latestSummaries) => { + // Calculate actual correct count from summaries for accuracy + const actualCorrect = latestSummaries.filter(q => q.isCorrect).length; + + trackingAssessmentService.createAssessmentTracking({ + userId: currentUser.username, + gameKey: gameKey, + gameTitle: 'True False Game', + level: currentLevel, + language: selectedLanguage || 'en', + totalQuestions: questions.length, + correctAnswers: actualCorrect, + totalScore: actualCorrect, + timeSpent: totalTimeSpent, + assessmentSummary: latestSummaries, + sessionId: sessionId, + subsessionId: subsessionId, + metadata: { + difficulty: difficultySettings.complexity, + levelFailed: false, + scorePercentage: scorePercentage + } + }); + return latestSummaries; + }); + } + + // End telemetry subsession for both pass and fail + await sessionTelemetryManager.endSubSession(); + + if (canAdvance) { + endSession(); + const newProgress = getGameProgress(gameKey); + if (newProgress.currentLevel > previousLevel) { + setShowLevelUp(true); + } + } + + setLevelFailed(false); + setIsGameComplete(true); + } + }, [currentQuestionIndex, questions.length, totalCorrect, isCorrect, previousLevel, gameKey]); + + const handleBackClick = async () => { + await sessionTelemetryManager.endSubSessionWithBackButton(); + setTimeout(() => onBack(), 100); + }; + + const resetGame = () => { + setCurrentQuestionIndex(0); + setScore(0); + setTotalCorrect(0); + setSelectedAnswer(null); + setShowFeedback(false); + setIsGameComplete(false); + setShowLevelUp(false); + setLevelFailed(false); + + setUsedQuestions(new Set()); + + // Reset tracking assessment state + setLevelStartTime(Date.now()); + setQuestionSummaries([]); + + if (selectedLanguage) { + const session = startSession(gameKey); + // Ensure only supported languages are passed (trueFalseDataLoader doesn't support 'hi') + const supportedLanguage: 'en' | 'te' | 'mr' | 'kn' = + (selectedLanguage === 'en' || selectedLanguage === 'te' || selectedLanguage === 'mr' || selectedLanguage === 'kn') + ? selectedLanguage + : 'en'; + const newQuestions = loadTrueFalseQuestions( + supportedLanguage, + difficultySettings.complexity as DifficultyLevel, + 10, + new Set() + ); + setQuestions(newQuestions); + } + }; + + + const handleLevelSelect = (level: number) => { + // Navigate to the specific level URL + navigate(`/true-false-game/level/${level}`); + }; + + const handleShowLevelSelector = () => { + navigate('/true-false-game'); + }; + + const calculateStars = () => { + if (questions.length === 0) return 0; + const percentage = (totalCorrect / questions.length) * 100; + if (percentage === 100) return 3; + if (percentage >= 90) return 2; + if (percentage >= 80) return 1; + // if (percentage >= 70) return 2; + // if (percentage >= 60) return 1; + return 0; + }; + + const getNewAchievements = () => { + const achievements = []; + if (questions.length > 0) { + if (totalCorrect === questions.length) { + achievements.push("Truth Seeker - Perfect Score!"); + } + if (totalCorrect >= Math.floor(questions.length * 0.8)) { + achievements.push("Fact Finder - Great Accuracy!"); + } + } + if (showLevelUp) { + achievements.push(`Level Up! Advanced to next level!`); + } + return achievements; + }; + + // Show loading state while fetching backend level + if (isLoadingLevel && selectedLanguage) { + return ( +
+
Loading...
+
+ ); + } + + // Show preview screen first (before level selector) + // For individual games: Hide preview for level 2+ if level 1 has any progress > 0% + // Show preview only if: (backend level is 1 AND level 1 has no progress) OR forcePreview is true + const shouldShowPreview = showPreview && selectedLanguage && + ((backendCurrentLevel === 1 && !level1HasProgress) || forcePreview); + + if (shouldShowPreview) { + return ( + { + setShowPreview(false); + setForcePreview(false); + }} + onBack={() => { + setForcePreview(false); + onBack(); + }} + difficulty={difficultySettings.complexity as "Easy" | "Medium" | "Hard"} + estimatedTime="5-8 min" + level={currentLevel} + /> + ); + } + + // Show level selector if level selector is requested + if (showLevelSelector) { + const levelSelectorCurrentLevel = gameProgress.currentLevel; + + return ( + { + setShowPreview(true); + onBack(); + }} + onDemo={() => { + setForcePreview(true); + setShowPreview(true); + }} + gameTitle="True or False Game" + gameKey={gameKey} + unlockAll={true} + /> + ); + } + + + // Show success screen when game is complete + if (isGameComplete) { + // If level failed, show try again screen + if (levelFailed) { + return ( + + ); + } + + // If level passed, show success screen + return ( + { + const nextLevel = Math.min(currentLevel + 1, languageLevels.maxLevels); + manuallyAdvanceLevel(gameKey, nextLevel); + + // Navigate to the next level URL + navigate(`/true-false-game/level/${nextLevel}`); + }} + /> + ); + } + + // Don't render if questions aren't loaded yet + if (!currentQuestion) { + return ( +
+
Loading...
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+ + +
+

+ True or False Game +

+
+ + + {selectedLevel !== null && selectedLevel !== gameProgress.currentLevel ? + `Practice Level ${selectedLevel}` : + `Level ${currentLevel} / ${languageLevels.maxLevels}` + } • {difficultySettings.complexity} + +
+
+ + {/* */} +
+ + {/* Main Content Card */} + + {/* Progress */} +
+ +
+ + {/* Game Area */} +
+ +
+
+
+
+ ); +} + diff --git a/src/lib/axl-explorations/src/components/games/TrueFalseGameCore.tsx b/src/lib/axl-explorations/src/components/games/TrueFalseGameCore.tsx new file mode 100644 index 00000000..3258b6d4 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/TrueFalseGameCore.tsx @@ -0,0 +1,179 @@ +import { Button } from "../ui/button"; +import { Check, X } from "lucide-react"; +import { Language } from "../../constants/languages"; +import { ContinueButton } from "./ContinueButton"; + +export interface TrueFalseQuestion { + statement: string; + isTrue: boolean; +} + +interface TrueFalseGameCoreProps { + currentQuestion: TrueFalseQuestion; + mode: 'game' | 'preview'; + selectedLanguage: Language; + selectedAnswer: boolean | null; + showFeedback: boolean; + isCorrect: boolean; + isPreview?: boolean; + demoStep?: string; + showHandPointer?: boolean; + disabled?: boolean; + onAnswerSelect: (answer: boolean) => void; + onCheckAnswer?: (answer: boolean) => void; + onContinue?: () => void; + feedbackLanguageOverride?: Language; +} + +export function TrueFalseGameCore({ + currentQuestion, + mode, + selectedLanguage, + selectedAnswer, + showFeedback, + isCorrect, + isPreview = false, + demoStep, + showHandPointer = false, + disabled = false, + onAnswerSelect, + onCheckAnswer, + onContinue, + feedbackLanguageOverride, +}: TrueFalseGameCoreProps) { + + // Localized text + const getLocalizedText = (key: 'successMessage' | 'failureMessage' | 'checkAnswer') => { + const messages = { + en: { + successMessage: '🎉 Correct!', + failureMessage: '😢 Oops! Wrong!', + checkAnswer: 'Check Answer' + }, + te: { + successMessage: '🎉 సరైనది!', + failureMessage: '😢 అయ్యో! తప్పు!', + checkAnswer: 'సమాధానం తనిఖీ చేయండి' + }, + kn: { + successMessage: '🎉 ಸರಿ!', + failureMessage: '😢 ಅಯ್ಯೋ! ತಪ್ಪು!', + checkAnswer: 'ಉತ್ತರವನ್ನು ಪರಿಶೀಲಿಸಿ' + }, + mr: { + successMessage: '🎉 बरोबर!', + failureMessage: '😢 अरेच्या! चुकीचे!', + checkAnswer: 'उत्तर तपासा' + } + }; + + const language = selectedLanguage || 'en'; + const localized = messages[language] || messages.en; + return localized[key] || messages.en[key]; + }; + + return ( +
+
+ {/* Statement Display */} +
+ {isPreview && (demoStep === 'showStatement' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4') ? ( +
+

+ {currentQuestion.statement} +

+
+ ) : !isPreview ? ( +

+ {currentQuestion.statement} +

+ ) : null} +
+ + {/* True/False Options */} +
+ {/* Hand Pointer - positioned absolutely so it doesn't affect centering */} + {showHandPointer && ( +
+
+ 👆 +
+
+ )} + +
+ + + +
+
+ + {/* Feedback Area - Show in both game and preview modes */} + {showFeedback && ( +
+
+

+ {getLocalizedText(isCorrect ? 'successMessage' : 'failureMessage')} +

+
+ + {/* Continue Button - Only show in game mode */} + +
+ )} +
+
+ ); +} diff --git a/src/lib/axl-explorations/src/components/games/TrueFalseGamePreview.tsx b/src/lib/axl-explorations/src/components/games/TrueFalseGamePreview.tsx new file mode 100644 index 00000000..5eba1e26 --- /dev/null +++ b/src/lib/axl-explorations/src/components/games/TrueFalseGamePreview.tsx @@ -0,0 +1,570 @@ +import { useState, useEffect, useRef } from "react"; +import { Card } from "../ui/card"; +import { Button } from "../ui/button"; +import { Progress } from "../ui/progress"; +import { ArrowLeft, Volume2, Sparkles, Clock, CheckCircle, Gamepad2, RotateCcw, Check, X } from "lucide-react"; +import { useLanguage } from "../../contexts/LanguageContext"; +import { useAudioLanguage } from "../../contexts/AudioLanguageContext"; +import { Language } from "../../constants/languages"; +import { CountdownTimer } from "../CountdownTimer"; +import { DemoCompletionScreen } from "../DemoCompletionScreen"; +import { playAudio, playTTS, playSuccessSound, playFailureSound, stopAllAudio, isAudioStopped, trackAudio } from "../../utils/audioUtils"; +import { TrueFalseGameCore } from "./TrueFalseGameCore"; + +interface TrueFalseGamePreviewProps { + onStartGame: () => void; + onBack: () => void; + difficulty?: "Easy" | "Medium" | "Hard"; + estimatedTime?: string; + level?: number; + hideHeader?: boolean; +} + +type PreviewPhase = 'countdown' | 'demo' | 'completion'; + +type DemoStep = + | 'instruction1' // Show instruction 1, play narration + | 'waitForReady' // Wait for user to click "I'm Ready" + | 'showStatement' // Show the statement after ready click + | 'instruction2' // After showing statement, show instruction 2, play narration + | 'instruction3' // After instruction 2, show instruction 3, play narration + | 'waitForAnswer' // Show options, wait for user to select + | 'wrongAnswer' // User selected wrong answer + | 'instruction4' // After correct answer, show final instruction + | 'complete'; // Demo run complete + +const gameInstructions = { + en: { + title: "True or False", + description: "Listen to the statement and decide if it's true or false!", + steps: [ + "📝 Read the statement carefully", + "🎯 Choose True or False", + "✨ Get points for correct answers!" + ], + instruction1: "Get ready! You'll see a statement to evaluate. Click 'I'm Ready' when prepared", + instruction2: "Good! Now read this statement carefully", + instruction3: "Now, decide if this statement is true or false", + instruction4: "Great job! You've completed the demo successfully!", + successMessage: "🎉 Correct!", + failureMessage: "😢 Oops! Wrong!", + narration1: "Get ready! You'll see a statement to evaluate. Click I'm Ready when prepared", + narration2: "Good! Now read this statement carefully", + narration3: "Now, decide if this statement is true or false", + narration4: "Great job! You've completed the demo successfully!", + howToPlay: "How to Play", + demo: { + audio: "The sky is blue", + statement: "The sky is blue", + correctAnswer: true, + explanation: "Correct! The sky is indeed blue." + } + }, + te: { + title: "సత్యం లేదా అసత్యం", + description: "వాక్యాన్ని విని అది సత్యమా లేదా అసత్యమా నిర్ణయించండి!", + steps: [ + "📝 వాక్యాన్ని జాగ్రత్తగా చదవండి", + "🎯 సత్యం లేదా అసత్యం ఎంచుకోండి", + "✨ సరైన సమాధానాలకు పాయింట్లు పొందండి!" + ], + instruction1: "సిద్ధంగా ఉండండి! మీరు వాక్యాన్ని అంచనా వేయబోతున్నారు. సిద్ధంగా ఉన్నప్పుడు 'నేను సిద్ధంగా ఉన్నాను' క్లిక్ చేయండి", + instruction2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + instruction3: "ఇప్పుడు, ఈ వాక్యం సత్యమా లేదా అసత్యమా నిర్ణయించండి", + instruction4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + successMessage: "🎉 సరైనది!", + failureMessage: "😢 అయ్యో! తప్పు!", + narration1: "సిద్ధంగా ఉండండి! మీరు వాక్యాన్ని అంచనా వేయబోతున్నారు. సిద్ధంగా ఉన్నప్పుడు నేను సిద్ధంగా ఉన్నాను క్లిక్ చేయండి", + narration2: "మంచిది! ఇప్పుడు ఈ వాక్యాన్ని జాగ్రత్తగా చదవండి", + narration3: "ఇప్పుడు, ఈ వాక్యం సత్యమా లేదా అసత్యమా నిర్ణయించండి", + narration4: "బాగా చేసారు! మీరు డెమోను విజయవంతంగా పూర్తి చేసారు!", + howToPlay: "ఎలా ఆడాలి", + demo: { + audio: "ఆకాశం నీలం రంగులో ఉంటుంది", + statement: "ఆకాశం నీలం రంగులో ఉంటుంది", + correctAnswer: true, + explanation: "సరైనది! ఆకాశం నిజంగా నీలం రంగులో ఉంటుంది." + } + }, + kn: { + title: "ಸತ್ಯ ಅಥವಾ ಸುಳ್ಳು", + description: "ಹೇಳಿಕೆಯನ್ನು ಕೇಳಿ ಅದು ಸತ್ಯವೇ ಅಥವಾ ಸುಳ್ಳೇ ನಿರ್ಧರಿಸಿ!", + steps: [ + "📝 ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + "🎯 ಸತ್ಯ ಅಥವಾ ಸುಳ್ಳು ಆರಿಸಿ", + "✨ ಸರಿಯಾದ ಉತ್ತರಗಳಿಗೆ ಅಂಕಗಳನ್ನು ಪಡೆಯಿರಿ!" + ], + instruction1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಹೇಳಿಕೆಯನ್ನು ಮೌಲ್ಯಮಾಪನ ಮಾಡಲಿದ್ದೀರಿ. ಸಿದ್ಧರಾದಾಗ 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' ಕ್ಲಿಕ್ ಮಾಡಿ", + instruction2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + instruction3: "ಈಗ, ಈ ಹೇಳಿಕೆ ಸತ್ಯವೇ ಅಥವಾ ಸುಳ್ಳೇ ನಿರ್ಧರಿಸಿ", + instruction4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + successMessage: "🎉 ಸರಿ!", + failureMessage: "😢 ಅಯ್ಯೋ! ತಪ್ಪು!", + narration1: "ಸಿದ್ಧರಾಗಿರಿ! ನೀವು ಹೇಳಿಕೆಯನ್ನು ಮೌಲ್ಯಮಾಪನ ಮಾಡಲಿದ್ದೀರಿ. ಸಿದ್ಧರಾದಾಗ ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ ಕ್ಲಿಕ್ ಮಾಡಿ", + narration2: "ಒಳ್ಳೆಯದು! ಈಗ ಈ ಹೇಳಿಕೆಯನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಓದಿ", + narration3: "ಈಗ, ಈ ಹೇಳಿಕೆ ಸತ್ಯವೇ ಅಥವಾ ಸುಳ್ಳೇ ನಿರ್ಧರಿಸಿ", + narration4: "ಅದ್ಭುತ! ನೀವು ಡೆಮೊವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ!", + howToPlay: "ಹೇಗೆ ಆಡುವುದು", + demo: { + audio: "ಆಕಾಶ ನೀಲಿ ಬಣ್ಣದಲ್ಲಿದೆ", + statement: "ಆಕಾಶ ನೀಲಿ ಬಣ್ಣದಲ್ಲಿದೆ", + correctAnswer: true, + explanation: "ಸರಿ! ಆಕಾಶ ನಿಜವಾಗಿಯೂ ನೀಲಿ ಬಣ್ಣದಲ್ಲಿದೆ." + } + }, + mr: { + title: "खरे किंवा खोटे", + description: "विधान ऐका आणि ते खरे आहे की खोटे ठरवा!", + steps: [ + "📝 विधान काळजीपूर्वक वाचा", + "🎯 खरे किंवा खोटे निवडा", + "✨ योग्य उत्तरांसाठी गुण मिळवा!" + ], + instruction1: "तयार रहा! तुम्ही विधानाचे मूल्यमापन करणार आहात. तयार असताना 'मी तयार आहे' क्लिक करा", + instruction2: "चांगले! आता हे विधान काळजीपूर्वक वाचा", + instruction3: "आता, हे विधान खरे आहे की खोटे ठरवा", + instruction4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + successMessage: "🎉 बरोबर!", + failureMessage: "😢 अरेच्या! चुकीचे!", + narration1: "तयार रहा! तुम्ही विधानाचे मूल्यमापन करणार आहात. तयार असताना मी तयार आहे क्लिक करा", + narration2: "चांगले! आता हे विधान काळजीपूर्वक वाचा", + narration3: "आता, हे विधान खरे आहे की खोटे ठरवा", + narration4: "उत्कृष्ट! तुम्ही डेमो यशस्वीरित्या पूर्ण केले!", + howToPlay: "कसे खेळायचे", + demo: { + audio: "आकाश निळ्या रंगाचे आहे", + statement: "आकाश निळ्या रंगाचे आहे", + correctAnswer: true, + explanation: "बरोबर! आकाश खरोखर निळ्या रंगाचे आहे." + } + } +}; + +export function TrueFalseGamePreview({ + onStartGame, + onBack, + difficulty = "Easy", + estimatedTime = "5-8 min", + level = 1, + hideHeader = false +}: TrueFalseGamePreviewProps) { + const { selectedLanguage } = useLanguage(); + const { selectedAudioLanguage } = useAudioLanguage(); + const [previewPhase, setPreviewPhase] = useState('countdown'); + const [demoStep, setDemoStep] = useState('instruction1'); + const [successfulRuns, setSuccessfulRuns] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + const [showFeedback, setShowFeedback] = useState(false); + const [isCorrectAnswer, setIsCorrectAnswer] = useState(false); + const [hasClickedReady, setHasClickedReady] = useState(false); + const [isPlayingNarration, setIsPlayingNarration] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [showStatement, setShowStatement] = useState(false); + const [completionCount, setCompletionCount] = useState(0); + const [hasCompletedFirstCycle, setHasCompletedFirstCycle] = useState(false); + + const readyButtonRef = useRef(null); + const optionsRef = useRef(null); + + const audioLanguage = selectedAudioLanguage || 'en'; + const contentLanguage = selectedLanguage || 'en'; + const instructions = gameInstructions[contentLanguage]; + + // Create demo question + const demoQuestion = { + statement: instructions.demo.statement, + isTrue: instructions.demo.correctAnswer + }; + + // Handle countdown complete + const handleCountdownComplete = () => { + setPreviewPhase('demo'); + setDemoStep('instruction1'); + }; + + // Play narration using TTS + // Play narration using combined sentence games audio files with TTS fallback + const playNarration = async (text: string, step: number) => { + // Prevent multiple simultaneous narration calls + if (isPlayingNarration) { + console.warn('Narration already playing, skipping...'); + return; + } + + setIsPlayingNarration(true); + + // Use combined sentence games audio files for True or False + const gameName = 'Combined Sentence Games'; + const subGame = 'True or False'; + + try { + await playAudio({ + gameName, + subGame, + language: audioLanguage, + type: 'narration', + step + }, text); + } catch (error) { + console.warn('Audio playback failed, using TTS fallback:', error); + // Stop any existing speech synthesis before starting new one + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + } + await playTTS(text, audioLanguage); + } + + setIsPlayingNarration(false); + }; + + + + // Update current step based on demo step + useEffect(() => { + switch (demoStep) { + case 'instruction1': + case 'waitForReady': + setCurrentStep(0); + break; + case 'showStatement': + case 'instruction2': + case 'instruction3': + case 'waitForAnswer': + setCurrentStep(1); + break; + case 'instruction4': + case 'complete': + setCurrentStep(2); + break; + } + }, [demoStep]); + + // Initialize demo - play instruction 1 + useEffect(() => { + if (demoStep === 'instruction1' && previewPhase === 'demo') { + playNarration(instructions.narration1, 1); + setHasClickedReady(false); + setSelectedAnswer(null); + setShowFeedback(false); + setShowStatement(false); + } + }, [demoStep, previewPhase, instructions.narration1]); + + // When instruction 1 narration finishes, move to waitForReady + useEffect(() => { + if (demoStep === 'instruction1' && !isPlayingNarration) { + const timer = setTimeout(() => { + setDemoStep('waitForReady'); + setTimeout(() => { + readyButtonRef.current?.focus(); + }, 100); + }, 500); + return () => clearTimeout(timer); + } + }, [demoStep, isPlayingNarration]); + + // Handle ready button click + const handleReadyClick = async () => { + if (demoStep !== 'waitForReady' || hasClickedReady) return; + + setHasClickedReady(true); + + // Show statement + setShowStatement(true); + setDemoStep('showStatement'); + + // Wait a moment then show instruction 2 + setTimeout(async () => { + setDemoStep('instruction2'); + await playNarration(instructions.narration2, 2); + + setDemoStep('instruction3'); + await playNarration(instructions.narration3, 3); + + setDemoStep('waitForAnswer'); + + setTimeout(() => { + optionsRef.current?.focus(); + }, 100); + }, 1500); + }; + + // Handle option click + const handleOptionClick = async (option: boolean) => { + if (demoStep !== 'waitForAnswer' || showFeedback) return; + + setSelectedAnswer(option); + setShowFeedback(true); + + const isCorrect = option === instructions.demo.correctAnswer; + setIsCorrectAnswer(isCorrect); + + if (isCorrect) { + await playSuccessSound(audioLanguage, { exactLanguage: true }); + + setDemoStep('instruction4'); + await playNarration(instructions.narration4, 4); + + const newSuccessfulRuns = successfulRuns + 1; + setSuccessfulRuns(newSuccessfulRuns); + const newCompletionCount = completionCount + 1; + setCompletionCount(newCompletionCount); + + // Wait a moment, then show completion screen after first successful run + setTimeout(() => { + setHasCompletedFirstCycle(true); + setPreviewPhase('completion'); + }, 2000); + } else { + await playFailureSound(audioLanguage, { exactLanguage: true }); + + setTimeout(() => { + setShowFeedback(false); + setSelectedAnswer(null); + }, 2000); + } + }; + + // Restart demo + const restartDemo = () => { + setDemoStep('instruction1'); + setSelectedAnswer(null); + setShowFeedback(false); + setIsCorrectAnswer(false); + setHasClickedReady(false); + setShowStatement(false); + setCurrentStep(0); + }; + + // Help button click + const handleHelpClick = () => { + restartDemo(); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + stopAllAudio(); + }; + }, []); + + // Cleanup on page unload only (no tab visibility handling - matches combined games behavior) + useEffect(() => { + const handleBeforeUnload = () => { + stopAllAudio(); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + const showReadyButton = (demoStep === 'waitForReady' || demoStep === 'instruction1') && !hasClickedReady; + const showStatementDisplay = showStatement && (demoStep === 'showStatement' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4'); + const showOptions = showStatement && (demoStep === 'showStatement' || demoStep === 'instruction2' || demoStep === 'instruction3' || demoStep === 'waitForAnswer' || demoStep === 'instruction4'); + + // Skip demo handler + const handleSkipDemo = () => { + stopAllAudio(); + onStartGame(); + }; + + // Back handler + const handleBack = () => { + stopAllAudio(); + onBack(); + }; + + // Start game handler + const handleStartGame = () => { + stopAllAudio(); + onStartGame(); + }; + + // Replay demo handler + const handleReplayDemo = () => { + stopAllAudio(); + setHasCompletedFirstCycle(false); + setCompletionCount(0); + setPreviewPhase('countdown'); + }; + + // Render completion phase + if (previewPhase === 'completion') { + return ( + + ); + } + + // Render countdown phase + if (previewPhase === 'countdown') { + return ( +
+
+ {!hideHeader && ( +
+ +
+ )} + +
+
+ ); + } + + return ( + <> + +
+
+ {/* Header */} + {!hideHeader && ( +
+ + +
+

+ {instructions.title} +

+
+ + + {contentLanguage === 'en' ? 'Level' : contentLanguage === 'te' ? 'స్థాయి' : contentLanguage === 'kn' ? 'ಮಟ್ಟ' : 'पातळी'} {level} • {difficulty.toLowerCase()} • {estimatedTime} + +
+
+ +
+
+ )} + + {/* Main Content Card */} + + {/* How to Play Section - Centered */} +
+
+
+ +
+

+ {instructions.howToPlay} +

+
+ +
+ + {/* Demo Panel - Full width */} +
+
= 1 ? 'h-[420px]' : 'h-[500px]' + }`}> + {/* Ready Button */} + {showReadyButton && ( +
+
+ {contentLanguage === 'en' ? "I'm Ready" : contentLanguage === 'te' ? 'నేను సిద్ధంగా ఉన్నాను' : contentLanguage === 'kn' ? 'ನಾನು ಸಿದ್ಧವಾಗಿದ್ದೇನೆ' : 'मी तयार आहे'} +
+ {demoStep === 'waitForReady' && ( +
+ 👆 +
+ )} +
+ )} + + {/* True/False Options */} + {showOptions && ( +
+ handleOptionClick(answer)} + onContinue={() => {}} + feedbackLanguageOverride={audioLanguage} + /> +
+ )} +
+
+ + {/* Bottom Section - Fixed Buttons */} +
+ {/* Skip Demo Button - Bottom Left */} + + + {/* Start Game Button - Bottom Right */} + +
+
+
+
+ + ); +} + +export default TrueFalseGamePreview; \ No newline at end of file diff --git a/src/lib/axl-explorations/src/components/levelConfigs/levelConfigs.ts b/src/lib/axl-explorations/src/components/levelConfigs/levelConfigs.ts new file mode 100644 index 00000000..feb17c32 --- /dev/null +++ b/src/lib/axl-explorations/src/components/levelConfigs/levelConfigs.ts @@ -0,0 +1,48 @@ +// Level configurations for different game themes + +export interface LevelConfig { + name: string; + image: string; +} + +// Space theme (Letter Launcher) - 10 levels +export const SPACE_LEVEL_CONFIG: Record = { + 1: { name: "Moon", image: "🌙" }, + 2: { name: "Mars", image: "🔴" }, + 3: { name: "Jupiter", image: "🪐" }, + 4: { name: "Saturn", image: "🪐" }, + 5: { name: "Venus", image: "🟠" }, + 6: { name: "Uranus", image: "🔵" }, + 7: { name: "Neptune", image: "🔵" }, + 8: { name: "Mercury", image: "⚫" }, + 9: { name: "Pluto", image: "⚪" }, + 10: { name: "Sun", image: "☀️" }, +}; + +// Detective theme - 10 levels +export const DETECTIVE_LEVEL_CONFIG: Record = { + 1: { name: "Case 1", image: "🔍" }, + 2: { name: "Case 2", image: "🔍" }, + 3: { name: "Case 3", image: "🔍" }, + 4: { name: "Case 4", image: "🔍" }, + 5: { name: "Case 5", image: "🔍" }, + 6: { name: "Case 6", image: "🔍" }, + 7: { name: "Case 7", image: "🔍" }, + 8: { name: "Case 8", image: "🔍" }, + 9: { name: "Case 9", image: "🔍" }, + 10: { name: "Case 10", image: "🔍" }, +}; + +// Superhero theme - 10 levels +export const SUPERHERO_LEVEL_CONFIG: Record = { + 1: { name: "Mission 1", image: "🦸" }, + 2: { name: "Mission 2", image: "🦸" }, + 3: { name: "Mission 3", image: "🦸" }, + 4: { name: "Mission 4", image: "🦸" }, + 5: { name: "Mission 5", image: "🦸" }, + 6: { name: "Mission 6", image: "🦸" }, + 7: { name: "Mission 7", image: "🦸" }, + 8: { name: "Mission 8", image: "🦸" }, + 9: { name: "Mission 9", image: "🦸" }, + 10: { name: "Mission 10", image: "🦸" }, +}; diff --git a/src/lib/axl-explorations/src/components/ui/PlanetIcon.tsx b/src/lib/axl-explorations/src/components/ui/PlanetIcon.tsx new file mode 100644 index 00000000..1d9a0e6c --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/PlanetIcon.tsx @@ -0,0 +1,111 @@ +interface PlanetIconProps { + level?: number; + destination?: string; + className?: string; + alt?: string; + animated?: boolean; +} + +const PLANET_CONFIG: Record = { + 1: { image: '/images/moon.png', label: 'Moon' }, + 2: { image: '/images/mars.png', label: 'Mars' }, + 3: { image: '/images/jupiter.png', label: 'Jupiter' }, + 4: { image: '/images/saturn.png', label: 'Saturn' }, + 5: { image: '/images/venus.png', label: 'Venus' }, + 6: { image: '/images/uranus.png', label: 'Uranus' }, + 7: { image: '/images/neptune.png', label: 'Neptune' }, + 8: { image: '/images/mercury.png', label: 'Mercury' }, + 9: { image: '/images/pluto.png', label: 'Pluto' }, + 10: { image: '/images/sun.png', label: 'Sun' }, +}; + +export function PlanetIcon({ level, destination, className = 'text-4xl', alt, animated = false }: PlanetIconProps) { + // Map destination names to levels + const destinationToLevel: Record = { + 'moon': 1, + 'mars': 2, + 'jupiter': 3, + 'saturn': 4, + 'venus': 5, + 'uranus': 6, + 'neptune': 7, + 'mercury': 8, + 'pluto': 9, + 'sun': 10 + }; + + const resolvedLevel = level ?? (destination ? destinationToLevel[destination.toLowerCase()] : 1); + const config = PLANET_CONFIG[resolvedLevel || 1]; + + // Fallback to emoji for levels without images + if (!config) { + return 🪐; + } + + // Static version + if (!animated) { + return ( + + {alt + + ); + } + + // Animated version (for story preview) + return ( + + {/* Glow effect */} + + + {/* Planet image with float animation */} + {alt + + {/* Sparkles */} + + + + + + + + + + + + + + ); +} + +export default PlanetIcon; + diff --git a/src/lib/axl-explorations/src/components/ui/ReplayButton.tsx b/src/lib/axl-explorations/src/components/ui/ReplayButton.tsx new file mode 100644 index 00000000..654dad0c --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/ReplayButton.tsx @@ -0,0 +1,48 @@ +import { Button } from "./button"; +import { RotateCcw } from "lucide-react"; +import { Language } from "../../constants/languages"; + +interface ReplayButtonProps { + onClick: () => void; + language?: Language; + className?: string; + variant?: "default" | "transparent"; +} + +const replayTranslations = { + en: "Replay", + te: "మళ్లీ ప్లే చేయండి", + kn: "ಮತ್ತೆ ಪ್ಲೇ ಮಾಡಿ", + mr: "पुन्हा प्ले करा", +}; + +export function ReplayButton({ + onClick, + language = 'en', + className = "", + variant = "transparent" +}: ReplayButtonProps) { + const buttonText = replayTranslations[language] || replayTranslations.en; + + const baseClassName = variant === "transparent" + ? "bg-white/20 backdrop-blur-sm text-white hover:bg-white/30 border border-white/20 text-sm px-3 py-2" + : ""; + + return ( + + ); +} + diff --git a/src/lib/axl-explorations/src/components/ui/ResourceRequirementCard.tsx b/src/lib/axl-explorations/src/components/ui/ResourceRequirementCard.tsx new file mode 100644 index 00000000..cb263b8d --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/ResourceRequirementCard.tsx @@ -0,0 +1,80 @@ +import React from 'react'; + +interface ResourceRequirementCardProps { + /** Left icon component (e.g., Fuel or Star icon) */ + leftIcon: React.ReactNode; + /** Label text to display next to the icon */ + label: string; + /** Right icon/image component (e.g., PlanetIcon or image) */ + rightIcon: React.ReactNode; + /** Progress value (0-100), defaults to 100 */ + progress?: number; + /** Alignment for the card container - 'end' aligns with bottom, 'center' aligns with center */ + alignment?: 'end' | 'center'; + /** Additional className for the container */ + className?: string; +} + +/** + * Common component for displaying resource requirement cards + * Used in Letter Launcher (Fuel Needed) and Odd One Out (Stars needed) story previews + */ +export function ResourceRequirementCard({ + leftIcon, + label, + rightIcon, + progress = 100, + alignment = 'end', + className = '' +}: ResourceRequirementCardProps) { + const alignmentClass = alignment === 'center' ? 'items-center' : 'items-end'; + + return ( + <> + {/* Desktop: Card in center */} +
+
+
+
+ {leftIcon} +
{label}
+
+ {rightIcon} +
+
+
+
+
+
+ + {/* Mobile: Card in same line with characters */} +
+
+
+
+ {leftIcon} +
{label}
+
+ {rightIcon} +
+
+
+
+
+
+ + ); +} + diff --git a/src/lib/axl-explorations/src/components/ui/accordion.tsx b/src/lib/axl-explorations/src/components/ui/accordion.tsx new file mode 100644 index 00000000..bf666b8f --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/accordion.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "../../lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/src/lib/axl-explorations/src/components/ui/alert-dialog.tsx b/src/lib/axl-explorations/src/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..48eb4a19 --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "../../lib/utils" +import { buttonVariants } from "./button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/src/lib/axl-explorations/src/components/ui/alert.tsx b/src/lib/axl-explorations/src/components/ui/alert.tsx new file mode 100644 index 00000000..e9286a33 --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "../../lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/src/lib/axl-explorations/src/components/ui/aspect-ratio.tsx b/src/lib/axl-explorations/src/components/ui/aspect-ratio.tsx new file mode 100644 index 00000000..c4abbf37 --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,5 @@ +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/src/lib/axl-explorations/src/components/ui/avatar.tsx b/src/lib/axl-explorations/src/components/ui/avatar.tsx new file mode 100644 index 00000000..1b8d07f9 --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/avatar.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "../../lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/lib/axl-explorations/src/components/ui/badge.tsx b/src/lib/axl-explorations/src/components/ui/badge.tsx new file mode 100644 index 00000000..a4483383 --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "../../lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/src/lib/axl-explorations/src/components/ui/breadcrumb.tsx b/src/lib/axl-explorations/src/components/ui/breadcrumb.tsx new file mode 100644 index 00000000..dd592c03 --- /dev/null +++ b/src/lib/axl-explorations/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "../../lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>