From 1c8d5855e609214341163d34989545d23a6b3e34 Mon Sep 17 00:00:00 2001 From: michael Date: Tue, 3 Mar 2026 17:59:56 -0800 Subject: [PATCH] initial commit --- .changeset/README.md | 8 + .changeset/config.json | 11 + .changeset/initial-release.md | 11 + .github/workflows/ci.yml | 52 + .github/workflows/release.yml | 37 + .gitignore | 27 + package-lock.json | 5378 +++++++++++++++++ package.json | 35 + packages/react-native/package.json | 49 + .../src/components/DTAccordion.tsx | 276 + .../react-native/src/components/DTButton.tsx | 151 + .../react-native/src/components/DTCard.tsx | 221 + .../src/components/DTCheckbox.tsx | 140 + .../react-native/src/components/DTChip.tsx | 95 + .../react-native/src/components/DTDrawer.tsx | 279 + .../react-native/src/components/DTGallery.tsx | 366 ++ .../react-native/src/components/DTHexagon.tsx | 210 + .../react-native/src/components/DTLabel.tsx | 313 + .../src/components/DTMediaFrame.tsx | 210 + .../react-native/src/components/DTMenu.tsx | 371 ++ .../react-native/src/components/DTModal.tsx | 115 + .../src/components/DTProgressBar.tsx | 183 + .../src/components/DTQuantityStepper.tsx | 234 + .../src/components/DTRadioGroup.tsx | 229 + .../src/components/DTSearchInput.tsx | 163 + .../react-native/src/components/DTSwitch.tsx | 152 + .../src/components/DTTextInput.tsx | 172 + packages/react-native/src/index.ts | 77 + .../src/theme/DTThemeProvider.tsx | 80 + packages/react-native/src/theme/colors.ts | 69 + packages/react-native/src/theme/paperTheme.ts | 149 + packages/react-native/src/theme/typography.ts | 190 + packages/react-native/src/utils/bevelPaths.ts | 152 + .../src/utils/useComponentLayout.ts | 33 + .../react-native/src/utils/variantColors.ts | 27 + packages/react-native/tsconfig.json | 15 + packages/tokens/package.json | 36 + packages/tokens/src/brands/classic.ts | 66 + packages/tokens/src/brands/dt.ts | 66 + packages/tokens/src/brands/supra.ts | 66 + packages/tokens/src/index.ts | 46 + packages/tokens/src/scripts/generate-css.ts | 129 + packages/tokens/src/types.ts | 65 + packages/tokens/tsconfig.json | 8 + .../fonts/Tektur-VariableFont_wdth,wght.ttf | Bin 0 -> 162656 bytes packages/web/package.json | 30 + packages/web/scripts/build.js | 87 + packages/web/src/base.css | 31 + packages/web/src/components/bevels.css | 290 + packages/web/src/components/elevation.css | 96 + packages/web/src/components/forms-dt.css | 329 + packages/web/src/components/glows.css | 80 + packages/web/src/dt-font.css | 9 + packages/web/src/index.css | 21 + tsconfig.base.json | 16 + turbo.json | 16 + 56 files changed, 11767 insertions(+) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 .changeset/initial-release.md create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 packages/react-native/package.json create mode 100644 packages/react-native/src/components/DTAccordion.tsx create mode 100644 packages/react-native/src/components/DTButton.tsx create mode 100644 packages/react-native/src/components/DTCard.tsx create mode 100644 packages/react-native/src/components/DTCheckbox.tsx create mode 100644 packages/react-native/src/components/DTChip.tsx create mode 100644 packages/react-native/src/components/DTDrawer.tsx create mode 100644 packages/react-native/src/components/DTGallery.tsx create mode 100644 packages/react-native/src/components/DTHexagon.tsx create mode 100644 packages/react-native/src/components/DTLabel.tsx create mode 100644 packages/react-native/src/components/DTMediaFrame.tsx create mode 100644 packages/react-native/src/components/DTMenu.tsx create mode 100644 packages/react-native/src/components/DTModal.tsx create mode 100644 packages/react-native/src/components/DTProgressBar.tsx create mode 100644 packages/react-native/src/components/DTQuantityStepper.tsx create mode 100644 packages/react-native/src/components/DTRadioGroup.tsx create mode 100644 packages/react-native/src/components/DTSearchInput.tsx create mode 100644 packages/react-native/src/components/DTSwitch.tsx create mode 100644 packages/react-native/src/components/DTTextInput.tsx create mode 100644 packages/react-native/src/index.ts create mode 100644 packages/react-native/src/theme/DTThemeProvider.tsx create mode 100644 packages/react-native/src/theme/colors.ts create mode 100644 packages/react-native/src/theme/paperTheme.ts create mode 100644 packages/react-native/src/theme/typography.ts create mode 100644 packages/react-native/src/utils/bevelPaths.ts create mode 100644 packages/react-native/src/utils/useComponentLayout.ts create mode 100644 packages/react-native/src/utils/variantColors.ts create mode 100644 packages/react-native/tsconfig.json create mode 100644 packages/tokens/package.json create mode 100644 packages/tokens/src/brands/classic.ts create mode 100644 packages/tokens/src/brands/dt.ts create mode 100644 packages/tokens/src/brands/supra.ts create mode 100644 packages/tokens/src/index.ts create mode 100644 packages/tokens/src/scripts/generate-css.ts create mode 100644 packages/tokens/src/types.ts create mode 100644 packages/tokens/tsconfig.json create mode 100644 packages/web/fonts/Tektur-VariableFont_wdth,wght.ttf create mode 100644 packages/web/package.json create mode 100644 packages/web/scripts/build.js create mode 100644 packages/web/src/base.css create mode 100644 packages/web/src/components/bevels.css create mode 100644 packages/web/src/components/elevation.css create mode 100644 packages/web/src/components/forms-dt.css create mode 100644 packages/web/src/components/glows.css create mode 100644 packages/web/src/dt-font.css create mode 100644 packages/web/src/index.css create mode 100644 tsconfig.base.json create mode 100644 turbo.json diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..654c6d4 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets). + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md). diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..ba0f177 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.3/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.changeset/initial-release.md b/.changeset/initial-release.md new file mode 100644 index 0000000..430ae2e --- /dev/null +++ b/.changeset/initial-release.md @@ -0,0 +1,11 @@ +--- +"@dangerousthings/tokens": minor +"@dangerousthings/web": minor +"@dangerousthings/react-native": minor +--- + +Initial release of the DT design system monorepo. + +- `@dangerousthings/tokens`: Canonical design tokens for DT, Classic, and Supra brands with CSS generation +- `@dangerousthings/web`: Web CSS theme with bevels, glows, form styles, and Tektur font +- `@dangerousthings/react-native`: 18 React Native themed components (DTButton, DTCard, DTTextInput, etc.) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..678840d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 20, 22] + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build all packages + run: npx turbo run build + + - name: Typecheck + run: npx turbo run typecheck + + publish-check: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - run: npm ci + - run: npx turbo run build + + - name: Check for changesets + run: npx changeset status diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..68042e6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +name: Release + +on: + push: + branches: [main] + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + id-token: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + registry-url: https://registry.npmjs.org + + - run: npm ci + - run: npx turbo run build + + - name: Create release PR or publish + uses: changesets/action@v1 + with: + publish: npx changeset publish + title: "chore: version packages" + commit: "chore: version packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e864920 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Dependencies +node_modules/ + +# Build output +dist/ +*.tsbuildinfo + +# Turbo cache +.turbo/ + +# OS files +.DS_Store +Thumbs.db + +# Editor +.idea/ +.vscode/ +*.swp +*.swo + +# Env +.env +.env.local + +# Logs +*.log +npm-debug.log* diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8499018 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5378 @@ +{ + "name": "dt-design-system", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dt-design-system", + "version": "0.0.0", + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "devDependencies": { + "@changesets/cli": "^2.30.0", + "turbo": "^2.4.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/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", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-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", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "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", + "peer": true, + "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", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@callstack/react-theme-provider": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.9.tgz", + "integrity": "sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^3.2.0", + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, + "node_modules/@changesets/apply-release-plan": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.1.0.tgz", + "integrity": "sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/config": "^3.1.3", + "@changesets/get-version-range-type": "^0.4.0", + "@changesets/git": "^3.0.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "detect-indent": "^6.0.0", + "fs-extra": "^7.0.1", + "lodash.startcase": "^4.4.0", + "outdent": "^0.5.0", + "prettier": "^2.7.1", + "resolve-from": "^5.0.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/assemble-release-plan": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.9.tgz", + "integrity": "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/changelog-git": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", + "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0" + } + }, + "node_modules/@changesets/cli": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.30.0.tgz", + "integrity": "sha512-5D3Nk2JPqMI1wK25pEymeWRSlSMdo5QOGlyfrKg0AOufrUcjEE3RQgaCpHoBiM31CSNrtSgdJ0U6zL1rLDDfBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/apply-release-plan": "^7.1.0", + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/changelog-git": "^0.2.1", + "@changesets/config": "^3.1.3", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/get-release-plan": "^4.0.15", + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.7", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@changesets/write": "^0.4.0", + "@inquirer/external-editor": "^1.0.2", + "@manypkg/get-packages": "^1.1.3", + "ansi-colors": "^4.1.3", + "enquirer": "^2.4.1", + "fs-extra": "^7.0.1", + "mri": "^1.2.0", + "package-manager-detector": "^0.2.0", + "picocolors": "^1.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.3", + "spawndamnit": "^3.0.1", + "term-size": "^2.1.0" + }, + "bin": { + "changeset": "bin.js" + } + }, + "node_modules/@changesets/config": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.3.tgz", + "integrity": "sha512-vnXjcey8YgBn2L1OPWd3ORs0bGC4LoYcK/ubpgvzNVr53JXV5GiTVj7fWdMRsoKUH7hhhMAQnsJUqLr21EncNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/logger": "^0.1.1", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1", + "micromatch": "^4.0.8" + } + }, + "node_modules/@changesets/errors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", + "dev": true, + "license": "MIT", + "dependencies": { + "extendable-error": "^0.1.5" + } + }, + "node_modules/@changesets/get-dependents-graph": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.3.tgz", + "integrity": "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "picocolors": "^1.1.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/get-release-plan": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.15.tgz", + "integrity": "sha512-Q04ZaRPuEVZtA+auOYgFaVQQSA98dXiVe/yFaZfY7hoSmQICHGvP0TF4u3EDNHWmmCS4ekA/XSpKlSM2PyTS2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/config": "^3.1.3", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.7", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/get-version-range-type": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/git": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", + "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@manypkg/get-packages": "^1.1.3", + "is-subdir": "^1.1.1", + "micromatch": "^4.0.8", + "spawndamnit": "^3.0.1" + } + }, + "node_modules/@changesets/logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/parse": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.3.tgz", + "integrity": "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "js-yaml": "^4.1.1" + } + }, + "node_modules/@changesets/parse/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@changesets/parse/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@changesets/pre": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", + "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1" + } + }, + "node_modules/@changesets/read": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.7.tgz", + "integrity": "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/parse": "^0.4.3", + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "p-filter": "^2.1.0", + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/should-skip-package": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", + "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/write": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", + "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "human-id": "^4.1.1", + "prettier": "^2.7.1" + } + }, + "node_modules/@dangerousthings/react-native": { + "resolved": "packages/react-native", + "link": true + }, + "node_modules/@dangerousthings/tokens": { + "resolved": "packages/tokens", + "link": true + }, + "node_modules/@dangerousthings/web": { + "resolved": "packages/web", + "link": true + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "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", + "peer": true, + "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/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.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/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + } + }, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@manypkg/get-packages": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@changesets/types": "^4.0.1", + "@manypkg/find-root": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "^11.0.0", + "read-yaml-file": "^1.1.0" + } + }, + "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/get-packages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@react-native/assets-registry": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.84.1.tgz", + "integrity": "sha512-lAJ6PDZv95FdT9s9uhc9ivhikW1Zwh4j9XdXM7J2l4oUA3t37qfoBmTSDLuPyE3Bi+Xtwa11hJm0BUTT2sc/gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/codegen": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.84.1.tgz", + "integrity": "sha512-n1RIU0QAavgCg1uC5+s53arL7/mpM+16IBhJ3nCFSd/iK5tUmCwxQDcIDC703fuXfpub/ZygeSjVN8bcOWn0gA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.25.3", + "hermes-parser": "0.32.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "tinyglobby": "^0.2.15", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.84.1.tgz", + "integrity": "sha512-f6a+mJEJ6Joxlt/050TqYUr7uRRbeKnz8lnpL7JajhpsgZLEbkJRjH8HY5QiLcRdUwWFtizml4V+vcO3P4RxoQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@react-native/dev-middleware": "0.84.1", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.83.3", + "metro-config": "^0.83.3", + "metro-core": "^0.83.3", + "semver": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "node_modules/@react-native/debugger-frontend": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.84.1.tgz", + "integrity": "sha512-rUU/Pyh3R5zT0WkVgB+yA6VwOp7HM5Hz4NYE97ajFS07OUIcv8JzBL3MXVdSSjLfldfqOuPEuKUaZcAOwPgabw==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/debugger-shell": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.84.1.tgz", + "integrity": "sha512-LIGhh4q4ette3yW5OzmukNMYwmINYrRGDZqKyTYc/VZyNpblZPw72coXVHXdfpPT6+YlxHqXzn3UjFZpNODGCQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "debug": "^4.4.0", + "fb-dotslash": "0.5.8" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.84.1.tgz", + "integrity": "sha512-Z83ra+Gk6ElAhH3XRrv3vwbwCPTb04sPPlNpotxcFZb5LtRQZwT91ZQEXw3GOJCVIFp9EQ/gj8AQbVvtHKOUlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.84.1", + "@react-native/debugger-shell": "0.84.1", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.2.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^7.5.10" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.84.1.tgz", + "integrity": "sha512-7uVlPBE3uluRNRX4MW7PUJIO1LDBTpAqStKHU7LHH+GRrdZbHsWtOEAX8PiY4GFfBEvG8hEjiuTOqAxMjV+hDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.84.1.tgz", + "integrity": "sha512-UsTe2AbUugsfyI7XIHMQq4E7xeC8a6GrYwuK+NohMMMJMxmyM3JkzIk+GB9e2il6ScEQNMJNaj+q+i5za8itxQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.84.1.tgz", + "integrity": "sha512-/UPaQ4jl95soXnLDEJ6Cs6lnRXhwbxtT4KbZz+AFDees7prMV2NOLcHfCnzmTabf5Y3oxENMVBL666n4GMLcTA==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.84.1.tgz", + "integrity": "sha512-sJoDunzhci8ZsqxlUiKoLut4xQeQcmbIgvDHGQKeBz6uEq9HgU+hCWOijMRr6sLP0slQVfBAza34Rq7IbXZZOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "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", + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "peer": true, + "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", + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-native": { + "version": "0.72.8", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.72.8.tgz", + "integrity": "sha512-St6xA7+EoHN5mEYfdWnfYt0e8u6k2FR0P9s2arYgakQGFgU1f9FlPrIEcj0X24pLCF5c5i3WVuLCUdiCYHmOoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-native/virtualized-lists": "^0.72.4", + "@types/react": "*" + } + }, + "node_modules/@types/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.72.8", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz", + "integrity": "sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "peerDependencies": { + "react-native": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT", + "peer": true + }, + "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", + "peer": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT", + "peer": true + }, + "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-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": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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", + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT", + "peer": true + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "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", + "peer": true, + "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": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-parser": "0.32.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "license": "MIT", + "peer": true, + "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 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "peer": true + }, + "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", + "peer": true + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-windows": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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", + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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", + "peer": true + }, + "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", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001776", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz", + "integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==", + "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", + "peer": true + }, + "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", + "peer": true, + "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/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chromium-edge-launcher": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", + "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "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", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "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", + "peer": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "peer": true, + "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/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", + "peer": true, + "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", + "peer": true + }, + "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", + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "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==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", + "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.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", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "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", + "peer": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.307", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", + "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", + "license": "ISC", + "peer": true + }, + "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", + "peer": true + }, + "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", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "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/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=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", + "peer": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "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", + "peer": true, + "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", + "peer": true + }, + "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", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "peer": true, + "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", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/extendable-error": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-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", + "peer": true + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-dotslash": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", + "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", + "license": "(MIT OR Apache-2.0)", + "peer": true, + "bin": { + "dotslash": "bin/dotslash" + }, + "engines": { + "node": ">=20" + } + }, + "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", + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "peer": true, + "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", + "peer": true, + "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", + "peer": true + }, + "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/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT", + "peer": 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", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "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", + "peer": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "peer": true, + "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", + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "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", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "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/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/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-compiler": { + "version": "250829098.0.9", + "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.9.tgz", + "integrity": "sha512-hZ5O7PDz1vQ99TS7HD3FJ9zVynfU1y+VWId6U1Pldvd8hmAYrNec/XLPYJKD3dLOW6NXak6aAQAuMuSo3ji0tQ==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-estree": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.32.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", + "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "peer": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "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", + "peer": true, + "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", + "peer": true + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT" + }, + "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", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-subdir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "better-path-resolve": "1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "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", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.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-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsc-safe-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD", + "peer": true + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "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", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/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", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "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.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT", + "peer": true + }, + "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/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", + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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", + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0", + "peer": true + }, + "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==", + "dev": true, + "license": "CC0-1.0" + }, + "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", + "peer": true + }, + "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", + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/metro": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.5.tgz", + "integrity": "sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.33.3", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.83.5", + "metro-cache": "0.83.5", + "metro-cache-key": "0.83.5", + "metro-config": "0.83.5", + "metro-core": "0.83.5", + "metro-file-map": "0.83.5", + "metro-resolver": "0.83.5", + "metro-runtime": "0.83.5", + "metro-source-map": "0.83.5", + "metro-symbolicate": "0.83.5", + "metro-transform-plugins": "0.83.5", + "metro-transform-worker": "0.83.5", + "mime-types": "^3.0.1", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.5.tgz", + "integrity": "sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.33.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", + "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", + "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.33.3" + } + }, + "node_modules/metro-cache": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.5.tgz", + "integrity": "sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng==", + "license": "MIT", + "peer": true, + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.83.5" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache-key": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.5.tgz", + "integrity": "sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-config": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.5.tgz", + "integrity": "sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w==", + "license": "MIT", + "peer": true, + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.83.5", + "metro-cache": "0.83.5", + "metro-core": "0.83.5", + "metro-runtime": "0.83.5", + "yaml": "^2.6.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-core": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.5.tgz", + "integrity": "sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.83.5" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-file-map": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.5.tgz", + "integrity": "sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.5.tgz", + "integrity": "sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-resolver": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.5.tgz", + "integrity": "sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-runtime": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.5.tgz", + "integrity": "sha512-f+b3ue9AWTVlZe2Xrki6TAoFtKIqw30jwfk7GQ1rDUBQaE0ZQ+NkiMEtb9uwH7uAjJ87U7Tdx1Jg1OJqUfEVlA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-source-map": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.5.tgz", + "integrity": "sha512-VT9bb2KO2/4tWY9Z2yeZqTUao7CicKAOps9LUg2aQzsz+04QyuXL3qgf1cLUVRjA/D6G5u1RJAlN1w9VNHtODQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.83.5", + "nullthrows": "^1.1.1", + "ob1": "0.83.5", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.5.tgz", + "integrity": "sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.83.5", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.5.tgz", + "integrity": "sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.5.tgz", + "integrity": "sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "metro": "0.83.5", + "metro-babel-transformer": "0.83.5", + "metro-cache": "0.83.5", + "metro-cache-key": "0.83.5", + "metro-minify-terser": "0.83.5", + "metro-source-map": "0.83.5", + "metro-transform-plugins": "0.83.5", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/hermes-estree": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", + "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", + "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.33.3" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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", + "peer": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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", + "peer": true + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "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", + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "license": "MIT", + "peer": true + }, + "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", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT" + }, + "node_modules/ob1": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.5.tgz", + "integrity": "sha512-vNKPYC8L5ycVANANpF/S+WZHpfnRWKx/F3AYP4QMn6ZJTh+l2HOrId0clNkEmua58NB9vmI9Qh7YOoV/4folYg==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "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", + "peer": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "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", + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, + "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", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "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", + "peer": true, + "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-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "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/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.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", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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", + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/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", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "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", + "peer": true + }, + "node_modules/react-native": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.84.1.tgz", + "integrity": "sha512-0PjxOyXRu3tZ8EobabxSukvhKje2HJbsZikR0U+pvS0pYZza2hXKjcSBiBdFN4h9D0S3v6a8kkrDK6WTRKMwzg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@react-native/assets-registry": "0.84.1", + "@react-native/codegen": "0.84.1", + "@react-native/community-cli-plugin": "0.84.1", + "@react-native/gradle-plugin": "0.84.1", + "@react-native/js-polyfills": "0.84.1", + "@react-native/normalize-colors": "0.84.1", + "@react-native/virtualized-lists": "0.84.1", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "0.32.0", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "hermes-compiler": "250829098.0.9", + "invariant": "^2.2.4", + "jest-environment-node": "^29.7.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.83.3", + "metro-source-map": "^0.83.3", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.27.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.15", + "whatwg-fetch": "^3.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.1", + "react": "^19.2.3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native-paper": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.15.0.tgz", + "integrity": "sha512-I/1CQLfW9VM0Oo5I5dQI/hjgf1I6q2S1wwgzAdsv6whAQ3zO97GWHwtgNh9se9j8zBOJ86afPTQKxxUL0IJd9A==", + "dev": true, + "license": "MIT", + "workspaces": [ + "example", + "docs" + ], + "dependencies": { + "@callstack/react-theme-provider": "^3.0.9", + "color": "^3.1.2", + "use-latest-callback": "^0.2.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-safe-area-context": "*" + } + }, + "node_modules/react-native-safe-area-context": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.7.0.tgz", + "integrity": "sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-svg": { + "version": "15.15.3", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.3.tgz", + "integrity": "sha512-/k4KYwPBLGcx2f5d4FjE+vCScK7QOX14cl2lIASJ28u4slHHtIhL0SZKU7u9qmRBHxTCKPoPBtN6haT1NENJNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "css-tree": "^1.1.3", + "warn-once": "0.1.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-yaml-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.6.1", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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", + "peer": true + }, + "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", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/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", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "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", + "peer": true, + "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", + "peer": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/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", + "peer": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "peer": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "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", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", + "peer": true + }, + "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.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "license": "MIT", + "peer": true, + "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", + "peer": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "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/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", + "peer": true, + "engines": { + "node": ">=0.10.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", + "peer": true, + "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", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawndamnit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", + "dev": true, + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" + } + }, + "node_modules/spawndamnit/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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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", + "peer": true, + "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", + "peer": true, + "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", + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "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", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "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", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "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", + "peer": true + }, + "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", + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT", + "peer": true + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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", + "peer": true + }, + "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/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", + "peer": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/turbo": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.8.13.tgz", + "integrity": "sha512-nyM99hwFB9/DHaFyKEqatdayGjsMNYsQ/XBNO6MITc7roncZetKb97MpHxWf3uiU+LB9c9HUlU3Jp2Ixei2k1A==", + "dev": true, + "license": "MIT", + "bin": { + "turbo": "bin/turbo" + }, + "optionalDependencies": { + "turbo-darwin-64": "2.8.13", + "turbo-darwin-arm64": "2.8.13", + "turbo-linux-64": "2.8.13", + "turbo-linux-arm64": "2.8.13", + "turbo-windows-64": "2.8.13", + "turbo-windows-arm64": "2.8.13" + } + }, + "node_modules/turbo-darwin-64": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.8.13.tgz", + "integrity": "sha512-PmOvodQNiOj77+Zwoqku70vwVjKzL34RTNxxoARjp5RU5FOj/CGiC6vcDQhNtFPUOWSAaogHF5qIka9TBhX4XA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-darwin-arm64": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.8.13.tgz", + "integrity": "sha512-kI+anKcLIM4L8h+NsM7mtAUpElkCOxv5LgiQVQR8BASyDFfc8Efj5kCk3cqxuxOvIqx0sLfCX7atrHQ2kwuNJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-linux-64": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.8.13.tgz", + "integrity": "sha512-j29KnQhHyzdzgCykBFeBqUPS4Wj7lWMnZ8CHqytlYDap4Jy70l4RNG46pOL9+lGu6DepK2s1rE86zQfo0IOdPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-linux-arm64": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.8.13.tgz", + "integrity": "sha512-OEl1YocXGZDRDh28doOUn49QwNe82kXljO1HXApjU0LapkDiGpfl3jkAlPKxEkGDSYWc8MH5Ll8S16Rf5tEBYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-windows-64": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.8.13.tgz", + "integrity": "sha512-717bVk1+Pn2Jody7OmWludhEirEe0okoj1NpRbSm5kVZz/yNN/jfjbxWC6ilimXMz7xoMT3IDfQFJsFR3PMANA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/turbo-windows-arm64": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.8.13.tgz", + "integrity": "sha512-R819HShLIT0Wj6zWVnIsYvSNtRNj1q9VIyaUz0P24SMcLCbQZIm1sV09F4SDbg+KCCumqD2lcaR2UViQ8SnUJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.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", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-latest-callback": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", + "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "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", + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT", + "peer": true + }, + "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", + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warn-once": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", + "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", + "dev": true, + "license": "MIT" + }, + "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", + "peer": true + }, + "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/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", + "peer": true, + "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/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", + "peer": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "license": "ISC", + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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", + "peer": true, + "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/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC", + "peer": true + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "packages/react-native": { + "name": "@dangerousthings/react-native", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@dangerousthings/tokens": "*" + }, + "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-native": "^0.72.8", + "react-native-paper": "^5.15.0", + "react-native-safe-area-context": "^5.7.0", + "react-native-svg": "^15.15.3", + "typescript": "^5.9.3" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-native": ">=0.72.0", + "react-native-paper": ">=5.0.0", + "react-native-safe-area-context": ">=4.0.0", + "react-native-svg": ">=13.0.0" + }, + "peerDependenciesMeta": { + "expo-font": { + "optional": true + } + } + }, + "packages/tokens": { + "name": "@dangerousthings/tokens", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^25.3.3", + "typescript": "^5.7.0" + } + }, + "packages/web": { + "name": "@dangerousthings/web", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@dangerousthings/tokens": "*" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9bc52f0 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "dt-design-system", + "version": "0.0.0", + "private": true, + "description": "Dangerous Things design system — shared tokens, web CSS, and React Native components", + "repository": { + "type": "git", + "url": "https://github.com/dangerous-tac0s/dt-design-system" + }, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "scripts": { + "build": "turbo run build", + "build:tokens": "npm run build -w packages/tokens", + "build:web": "npm run build -w packages/web", + "build:react-native": "npm run build -w packages/react-native", + "clean": "turbo run clean", + "typecheck": "turbo run typecheck", + "lint": "turbo run lint", + "changeset": "changeset", + "version-packages": "changeset version", + "release": "turbo run build && changeset publish" + }, + "devDependencies": { + "@changesets/cli": "^2.30.0", + "turbo": "^2.4.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "packageManager": "npm@10.0.0" +} diff --git a/packages/react-native/package.json b/packages/react-native/package.json new file mode 100644 index 0000000..de0164e --- /dev/null +++ b/packages/react-native/package.json @@ -0,0 +1,49 @@ +{ + "name": "@dangerousthings/react-native", + "version": "0.1.0", + "description": "React Native themed components for the Dangerous Things design system", + "license": "MIT", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "clean": "rm -rf dist", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@dangerousthings/tokens": "*" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-native": ">=0.72.0", + "react-native-paper": ">=5.0.0", + "react-native-safe-area-context": ">=4.0.0", + "react-native-svg": ">=13.0.0" + }, + "peerDependenciesMeta": { + "expo-font": { + "optional": true + } + }, + "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-native": "^0.72.8", + "react-native-paper": "^5.15.0", + "react-native-safe-area-context": "^5.7.0", + "react-native-svg": "^15.15.3", + "typescript": "^5.9.3" + } +} diff --git a/packages/react-native/src/components/DTAccordion.tsx b/packages/react-native/src/components/DTAccordion.tsx new file mode 100644 index 0000000..75d64e6 --- /dev/null +++ b/packages/react-native/src/components/DTAccordion.tsx @@ -0,0 +1,276 @@ +/** + * DT Accordion Component + * + * Custom-built accordion with animated height expansion following + * the Dangerous Things angular aesthetic. + * + * Web CSS reference (FilterAccordion): + * - menu-item-clipped style headers with thick top border + * - Expand/collapse with height animation + * - Active sections switch to activeVariant color + */ + +import {useState, useCallback, useRef, useEffect} from 'react'; +import { + StyleSheet, + View, + ViewStyle, + StyleProp, + Pressable, + Animated, +} from 'react-native'; +import {Text} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +export interface DTAccordionSection { + /** + * Unique key for the section + */ + key: string; + /** + * Section header title + */ + title: string; + /** + * Section content (rendered when expanded) + */ + children: React.ReactNode; +} + +interface DTAccordionProps { + /** + * Array of accordion sections + */ + sections: DTAccordionSection[]; + /** + * Visual variant for section headers + * @default 'normal' + */ + variant?: DTVariant; + /** + * Variant color for active/expanded sections + * @default 'emphasis' + */ + activeVariant?: DTVariant; + /** + * Allow multiple sections open simultaneously + * @default false + */ + allowMultiple?: boolean; + /** + * Section keys to open initially + */ + initialOpenKeys?: string[]; + /** + * Called when a section is toggled + */ + onSectionToggle?: (key: string, isOpen: boolean) => void; + /** + * Additional styles + */ + style?: StyleProp; +} + +/** + * DT-styled Accordion with animated height expansion + * + * @example + * }, + * { key: 'color', title: 'Color', children: }, + * ]} + * variant="normal" + * /> + */ +export function DTAccordion({ + sections, + variant = 'normal', + activeVariant = 'emphasis', + allowMultiple = false, + initialOpenKeys, + onSectionToggle, + style, +}: DTAccordionProps) { + const [openKeys, setOpenKeys] = useState>( + new Set(initialOpenKeys), + ); + + const toggleSection = useCallback( + (key: string) => { + setOpenKeys(prev => { + const next = new Set(allowMultiple ? prev : []); + const isOpening = !prev.has(key); + if (isOpening) { + next.add(key); + } else { + next.delete(key); + } + onSectionToggle?.(key, isOpening); + return next; + }); + }, + [allowMultiple, onSectionToggle], + ); + + const inactiveColor = getVariantColor(variant); + const activeColor = getVariantColor(activeVariant); + + return ( + + {sections.map(section => ( + toggleSection(section.key)} + inactiveColor={inactiveColor} + activeColor={activeColor} + /> + ))} + + ); +} + +function AccordionSection({ + section, + isOpen, + onToggle, + inactiveColor, + activeColor, +}: { + section: DTAccordionSection; + isOpen: boolean; + onToggle: () => void; + inactiveColor: string; + activeColor: string; +}) { + const heightAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current; + const chevronAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current; + const [contentHeight, setContentHeight] = useState(0); + const [measured, setMeasured] = useState(false); + const sectionColor = isOpen ? activeColor : inactiveColor; + + useEffect(() => { + Animated.parallel([ + Animated.timing(heightAnim, { + toValue: isOpen ? 1 : 0, + duration: 250, + useNativeDriver: false, + }), + Animated.timing(chevronAnim, { + toValue: isOpen ? 1 : 0, + duration: 250, + useNativeDriver: true, + }), + ]).start(); + }, [isOpen, heightAnim, chevronAnim]); + + const animatedHeight = heightAnim.interpolate({ + inputRange: [0, 1], + outputRange: [0, contentHeight], + }); + + const chevronRotate = chevronAnim.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '180deg'], + }); + + return ( + + {/* Header */} + [ + styles.header, + { + borderColor: sectionColor, + borderTopColor: sectionColor, + opacity: pressed ? 0.7 : 1, + }, + ]}> + + {section.title} + + + + + + + {/* Animated content */} + + { + const h = e.nativeEvent.layout.height; + if (h > 0 && !measured) { + setContentHeight(h); + setMeasured(true); + if (!isOpen) { + heightAnim.setValue(0); + } + } + }}> + {section.children} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + gap: 4, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: DTColors.dark, + borderWidth: 1, + borderTopWidth: 5, + paddingVertical: 12, + paddingHorizontal: 16, + }, + title: { + fontWeight: '600', + letterSpacing: 0.5, + textTransform: 'uppercase', + fontSize: 14, + }, + contentWrapper: {}, + content: { + paddingHorizontal: 16, + paddingVertical: 12, + backgroundColor: DTColors.dark, + }, + chevron: { + justifyContent: 'center', + alignItems: 'center', + width: 24, + height: 24, + }, + chevronArrow: { + width: 0, + height: 0, + borderLeftWidth: 6, + borderRightWidth: 6, + borderTopWidth: 8, + borderLeftColor: 'transparent', + borderRightColor: 'transparent', + }, +}); diff --git a/packages/react-native/src/components/DTButton.tsx b/packages/react-native/src/components/DTButton.tsx new file mode 100644 index 0000000..5676549 --- /dev/null +++ b/packages/react-native/src/components/DTButton.tsx @@ -0,0 +1,151 @@ +/** + * DT Button Component + * + * A themed button following the Dangerous Things design language + * with SVG-based beveled corners. + */ + +import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native'; +import {Text} from 'react-native-paper'; +import Svg, {Path} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildButtonBevelPath} from '../utils/bevelPaths'; +import {useComponentLayout} from '../utils/useComponentLayout'; +import {useState} from 'react'; + +interface DTButtonProps { + /** + * Button content/label + */ + children: React.ReactNode; + /** + * Visual variant of the button + * @default 'normal' + */ + variant?: DTVariant; + /** + * Button display mode + * - 'outlined': Border with transparent background (default) + * - 'contained': Filled background + * @default 'outlined' + */ + mode?: 'outlined' | 'contained'; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Press handler + */ + onPress?: () => void; + /** + * Whether button is disabled + */ + disabled?: boolean; + /** + * Additional styles for container + */ + style?: StyleProp; + /** + * Border width + * @default 2 + */ + borderWidth?: number; + /** + * Bevel size in pixels + * @default 8 + */ + bevelSize?: number; +} + +/** + * DT-styled Button component with SVG beveled corners + * + * @example + * + * Scan NFC Tag + * + * + * @example + * + * View Results + * + */ +export function DTButton({ + children, + variant = 'normal', + mode = 'outlined', + color, + onPress, + disabled = false, + style, + borderWidth = 2, + bevelSize = 8, +}: DTButtonProps) { + const {dimensions, onLayout, hasDimensions} = useComponentLayout(); + const [pressed, setPressed] = useState(false); + + const accentColor = getVariantColor(variant, color); + const isContained = mode === 'contained'; + const bgColor = isContained ? accentColor : 'transparent'; + const textColor = isContained ? DTColors.dark : accentColor; + + const {width, height} = dimensions; + const opacity = disabled ? 0.5 : pressed ? 0.7 : 1; + + return ( + setPressed(true)} + onPressOut={() => setPressed(false)} + style={[{opacity}, style]}> + + {hasDimensions && ( + + + + )} + + {typeof children === 'string' ? ( + + {children} + + ) : ( + children + )} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + position: 'relative', + minHeight: 44, + }, + content: { + position: 'relative', + zIndex: 1, + paddingVertical: 12, + paddingHorizontal: 24, + alignItems: 'center', + justifyContent: 'center', + }, + label: { + fontWeight: '600', + letterSpacing: 1, + textTransform: 'uppercase', + }, +}); diff --git a/packages/react-native/src/components/DTCard.tsx b/packages/react-native/src/components/DTCard.tsx new file mode 100644 index 0000000..246bd71 --- /dev/null +++ b/packages/react-native/src/components/DTCard.tsx @@ -0,0 +1,221 @@ +/** + * DT Card Component + * + * A themed card following the Dangerous Things design language + * with SVG-based beveled corners matching the web storefront style. + * + * Web CSS reference: + * - Bottom-right bevel: 2em (~32px) + * - Bottom-left bevel: 1em (~16px) + * - Border width: 0.2em (~3px) + */ + +import {ReactNode} from 'react'; +import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native'; +import {Text} from 'react-native-paper'; +import Svg, {Path} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildCardBevelPath} from '../utils/bevelPaths'; +import {useComponentLayout} from '../utils/useComponentLayout'; + +interface DTCardProps { + children: ReactNode; + /** + * Color mode for the card accent + * @default 'normal' + */ + mode?: DTVariant; + /** + * Custom border color (overrides mode) + */ + borderColor?: string; + /** + * Card title (displayed in header) + */ + title?: string; + /** + * Whether to show the accent header bar + * @default true when title is provided + */ + showHeader?: boolean; + /** + * Additional styles for the card container + */ + style?: StyleProp; + /** + * Additional styles for the content area + */ + contentStyle?: StyleProp; + /** + * Inner content padding + * @default 16 + */ + padding?: number; + /** + * Border width in pixels + * @default 3 + */ + borderWidth?: number; + /** + * Bottom-right bevel size in pixels + * @default 32 + */ + bevelSize?: number; + /** + * Bottom-left bevel size in pixels (smaller accent bevel) + * @default 16 + */ + bevelSizeSmall?: number; + /** + * Background color + * @default '#000000' + */ + backgroundColor?: string; + /** + * Press handler + */ + onPress?: () => void; +} + +/** + * DT-styled Card component with SVG beveled corners + * + * @example + * + * NTAG215 + * + * + * @example + * + * Tap to scan + * + */ +export function DTCard({ + children, + mode = 'normal', + borderColor, + title, + showHeader, + style, + contentStyle, + padding = 16, + borderWidth = 3, + bevelSize = 32, + bevelSizeSmall = 16, + backgroundColor = '#000000', + onPress, +}: DTCardProps) { + const {dimensions, onLayout, hasDimensions} = useComponentLayout(); + const accentColor = getVariantColor(mode, borderColor); + const shouldShowHeader = showHeader ?? !!title; + + const {width, height} = dimensions; + + // Outer bevel path (full card shape) + const outerPath = hasDimensions + ? buildCardBevelPath(width, height, bevelSize, bevelSizeSmall, 0) + : ''; + // Inner bevel path at border inset (for background + frame cutout) + const innerPath = hasDimensions + ? buildCardBevelPath(width, height, bevelSize - borderWidth, bevelSizeSmall - borderWidth, borderWidth) + : ''; + + const content = ( + + {/* Background fill (behind content) */} + {hasDimensions && ( + + + + )} + + {shouldShowHeader && ( + + {title && ( + + {title} + + )} + + )} + {children} + + {/* Frame overlay (above content) — clips content at beveled border */} + {hasDimensions && ( + + + + )} + + ); + + if (onPress) { + return ( + ({opacity: pressed ? 0.8 : 1})}> + {content} + + ); + } + + return content; +} + +/** + * DT design system constants matching web CSS variables + */ +export const DTCardClipPath = { + // CSS-style clip path (for web reference) + css: `polygon( + 0% 0%, + 100% 0%, + 100% calc(100% - 2em), + calc(100% - 2em) 100%, + 1em 100%, + 0% calc(100% - 1em) + )`, + // Default bevel sizes in pixels (matching 2em and 1em at 16px base) + bevelSize: 32, + bevelSizeSmall: 16, + borderWidth: 3, +}; + +const styles = StyleSheet.create({ + container: { + position: 'relative', + }, + innerContainer: { + position: 'relative', + zIndex: 1, + overflow: 'hidden', + }, + header: { + paddingHorizontal: 16, + paddingVertical: 12, + }, + headerText: { + fontWeight: '700', + letterSpacing: 0.5, + }, + content: {}, + frameOverlay: { + zIndex: 2, + }, +}); diff --git a/packages/react-native/src/components/DTCheckbox.tsx b/packages/react-native/src/components/DTCheckbox.tsx new file mode 100644 index 0000000..b7815d0 --- /dev/null +++ b/packages/react-native/src/components/DTCheckbox.tsx @@ -0,0 +1,140 @@ +/** + * DT Checkbox Component + * + * Custom-built angular checkbox following the Dangerous Things design language. + * Uses SVG for beveled shape and checkmark — RNP Checkbox enforces + * rounded corners (borderRadius: 18) so we build from scratch. + * + * Uses diagonal-symmetry bevels (top-left + bottom-right) on the indicator. + */ + +import {StyleSheet, ViewStyle, TextStyle, StyleProp, Pressable} from 'react-native'; +import {Text} from 'react-native-paper'; +import Svg, {Path} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildBeveledRectPath} from '../utils/bevelPaths'; + +interface DTCheckboxProps { + /** + * Whether the checkbox is checked + */ + checked: boolean; + /** + * Press handler to toggle state + */ + onPress: () => void; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Label text displayed beside the checkbox + */ + label?: string; + /** + * Whether the checkbox is disabled + */ + disabled?: boolean; + /** + * Size of the checkbox in pixels + * @default 24 + */ + size?: number; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Additional styles for the container + */ + style?: StyleProp; + /** + * Additional styles for the label text + */ + labelStyle?: StyleProp; +} + +/** + * DT-styled Checkbox with beveled opposing corners (top-left + bottom-right) + * + * @example + * setChecked(!isChecked)} label="Remember me" /> + * + * @example + * + */ +export function DTCheckbox({ + checked, + onPress, + variant = 'normal', + label, + disabled = false, + size = 24, + color, + style, + labelStyle, +}: DTCheckboxProps) { + const accentColor = getVariantColor(variant, color); + const opacity = disabled ? 0.5 : 1; + const borderWidth = 2; + const bevelSize = Math.round(size * 0.3); + + // Outer beveled path (border) + const outerPath = buildBeveledRectPath(size, size, { + corners: {topLeft: bevelSize, bottomRight: bevelSize}, + strokeWidth: borderWidth, + }); + + return ( + [ + styles.container, + {opacity: pressed ? 0.7 : opacity}, + style, + ]}> + + {/* Beveled border */} + + {/* Checkmark path (only when checked) */} + {checked && ( + + )} + + {label && ( + + {label} + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + minHeight: 44, + paddingVertical: 8, + }, + label: { + fontSize: 14, + letterSpacing: 0.3, + }, +}); diff --git a/packages/react-native/src/components/DTChip.tsx b/packages/react-native/src/components/DTChip.tsx new file mode 100644 index 0000000..02eac8b --- /dev/null +++ b/packages/react-native/src/components/DTChip.tsx @@ -0,0 +1,95 @@ +/** + * DT Chip Component + * + * A themed chip/tag following the Dangerous Things design language. + * Useful for displaying chip types, statuses, and labels. + */ + +// React import not needed with new JSX transform +import { StyleSheet, ViewStyle, StyleProp } from 'react-native'; +import { Chip, ChipProps } from 'react-native-paper'; +import { DTColors } from '../theme/colors'; + +type DTChipVariant = 'normal' | 'emphasis' | 'warning' | 'success' | 'other'; + +interface DTChipProps extends Omit { + /** + * Visual variant of the chip + * @default 'normal' + */ + variant?: DTChipVariant; + /** + * Whether the chip is in selected state + * @default false + */ + selected?: boolean; + /** + * Additional styles + */ + style?: StyleProp; +} + +const variantColors: Record = { + normal: DTColors.modeNormal, + emphasis: DTColors.modeEmphasis, + warning: DTColors.modeWarning, + success: DTColors.modeSuccess, + other: DTColors.modeOther, +}; + +/** + * DT-styled Chip component + * + * @example + * NTAG215 + * + * @example + * Cloneable + * + * @example + * Non-Cloneable + */ +export function DTChip({ + variant = 'normal', + selected = false, + style, + children, + ...props +}: DTChipProps) { + const color = variantColors[variant]; + + const chipStyle: ViewStyle = { + backgroundColor: selected ? color : 'transparent', + borderColor: color, + borderWidth: 1, + borderRadius: 0, // Angular DT style + }; + + return ( + + {children} + + ); +} + +const styles = StyleSheet.create({ + chip: { + marginRight: 8, + marginBottom: 8, + }, + text: { + fontWeight: '600', + letterSpacing: 0.5, + fontSize: 12, + }, +}); diff --git a/packages/react-native/src/components/DTDrawer.tsx b/packages/react-native/src/components/DTDrawer.tsx new file mode 100644 index 0000000..8fcc205 --- /dev/null +++ b/packages/react-native/src/components/DTDrawer.tsx @@ -0,0 +1,279 @@ +/** + * DT Drawer Component + * + * Hybrid component: RNP Portal for layering + custom Animated panel + * for the sliding behavior. RNP has no sliding panel component. + * + * Uses the dual-SVG-path technique (outer border fill + inner dark fill) + * consistent with DTCard/DTMediaFrame for proper visual clipping at + * beveled corners. + * + * Web CSS reference (Aside.tsx): + * - Fixed sidebar sliding from right + * - Beveled left edges (top-left + bottom-left for right drawer) + * - Colored header bar + * - Overlay backdrop + * - transition: transform 200ms ease-in-out + */ + +import {useRef, useEffect} from 'react'; +import { + StyleSheet, + View, + ViewStyle, + StyleProp, + Pressable, + Animated, + BackHandler, + ScrollView, + useWindowDimensions, +} from 'react-native'; +import {Portal, Text} from 'react-native-paper'; +import Svg, {Path} from 'react-native-svg'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildDrawerBevelPath} from '../utils/bevelPaths'; + +interface DTDrawerProps { + /** + * Whether the drawer is visible + */ + visible: boolean; + /** + * Called when the drawer is dismissed + */ + onDismiss: () => void; + /** + * Heading text in the drawer header bar + */ + heading: string; + /** + * Visual variant for the header bar + * @default 'emphasis' + */ + headingVariant?: DTVariant; + /** + * Which side the drawer slides from + * @default 'right' + */ + position?: 'right' | 'left'; + /** + * Drawer panel width in pixels + * @default 400 + */ + width?: number; + /** + * Bevel size in pixels + * @default 32 + */ + bevelSize?: number; + /** + * Border width in pixels + * @default 2 + */ + borderWidth?: number; + /** + * Drawer content + */ + children: React.ReactNode; + /** + * Additional styles + */ + style?: StyleProp; +} + +/** + * DT-styled Drawer/Aside with beveled edges and slide animation + * + * @example + * setCartOpen(false)} heading="Cart"> + * + * + * + * @example + * + * + * + */ +export function DTDrawer({ + visible, + onDismiss, + heading, + headingVariant = 'emphasis', + position = 'right', + width: drawerWidth = 400, + bevelSize = 32, + borderWidth = 2, + children, + style, +}: DTDrawerProps) { + const slideAnim = useRef(new Animated.Value(0)).current; + const overlayAnim = useRef(new Animated.Value(0)).current; + const headerColor = getVariantColor(headingVariant); + const {width: screenWidth, height: screenHeight} = useWindowDimensions(); + const insets = useSafeAreaInsets(); + const effectiveWidth = Math.min(drawerWidth, screenWidth); + + useEffect(() => { + if (visible) { + Animated.parallel([ + Animated.timing(slideAnim, { + toValue: 1, + duration: 200, + useNativeDriver: true, + }), + Animated.timing(overlayAnim, { + toValue: 1, + duration: 200, + useNativeDriver: true, + }), + ]).start(); + } else { + Animated.parallel([ + Animated.timing(slideAnim, { + toValue: 0, + duration: 200, + useNativeDriver: true, + }), + Animated.timing(overlayAnim, { + toValue: 0, + duration: 200, + useNativeDriver: true, + }), + ]).start(); + } + }, [visible, slideAnim, overlayAnim]); + + // Android back button handling + useEffect(() => { + if (!visible) return; + const handler = BackHandler.addEventListener('hardwareBackPress', () => { + onDismiss(); + return true; + }); + return () => handler.remove(); + }, [visible, onDismiss]); + + const translateX = slideAnim.interpolate({ + inputRange: [0, 1], + outputRange: [ + position === 'right' ? effectiveWidth : -effectiveWidth, + 0, + ], + }); + + // Don't render when fully hidden + const pointerEvents = visible ? 'auto' : 'none'; + + return ( + + + {/* Overlay backdrop */} + + + + + {/* Drawer panel */} + + {/* Dual-SVG-path technique: outer border + inner dark fill */} + + + {/* Outer path: accent border color */} + + {/* Inner path: dark background (inset by borderWidth) */} + + + + + {/* Header bar */} + + {heading} + ({opacity: pressed ? 0.7 : 1})}> + + + + + {/* Content */} + + {children} + + + + + ); +} + +const styles = StyleSheet.create({ + overlay: { + backgroundColor: DTColors.overlay, + }, + panel: { + position: 'absolute', + top: 0, + bottom: 0, + }, + panelRight: { + right: 0, + }, + panelLeft: { + left: 0, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 16, + zIndex: 1, + }, + headerText: { + color: DTColors.dark, + fontWeight: '700', + fontSize: 18, + letterSpacing: 0.5, + }, + closeButton: { + color: DTColors.dark, + fontSize: 20, + fontWeight: '700', + paddingHorizontal: 8, + }, + content: { + flex: 1, + zIndex: 1, + }, + contentContainer: { + padding: 16, + }, +}); diff --git a/packages/react-native/src/components/DTGallery.tsx b/packages/react-native/src/components/DTGallery.tsx new file mode 100644 index 0000000..de0c636 --- /dev/null +++ b/packages/react-native/src/components/DTGallery.tsx @@ -0,0 +1,366 @@ +/** + * DT Gallery Component + * + * Custom-built image gallery with thumbnail navigation following + * the Dangerous Things design language. No RNP equivalent. + * + * Uses the dual-SVG-path technique for the main image frame, + * consistent with DTCard/DTMediaFrame. + * + * Web CSS reference (CyberGallery): + * - Horizontal thumbnail strip with arrow navigation + * - Active thumbnail with glow effect (box-shadow: 0 0 8px) + * - Beveled arrow buttons + * - Main image in beveled media frame + */ + +import {useRef, useCallback} from 'react'; +import { + StyleSheet, + View, + ViewStyle, + StyleProp, + Pressable, + Image, + FlatList, + Platform, +} from 'react-native'; +import Svg, {Path} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildButtonBevelPath, buildMediaFrameBevelPath} from '../utils/bevelPaths'; +import {useComponentLayout} from '../utils/useComponentLayout'; + +export interface DTGalleryItem { + /** + * Unique identifier + */ + id: string; + /** + * Image URI + */ + uri: string; + /** + * Accessibility label + */ + alt?: string; +} + +interface DTGalleryProps { + /** + * Gallery items + */ + items: DTGalleryItem[]; + /** + * Currently active item index + */ + activeIndex: number; + /** + * Called when an item is selected + */ + onSelect: (index: number) => void; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Thumbnail size in pixels + * @default 64 + */ + thumbnailSize?: number; + /** + * Bevel size for the main image frame + * @default 24 + */ + bevelSize?: number; + /** + * Border width for the main image frame + * @default 3 + */ + borderWidth?: number; + /** + * Additional styles + */ + style?: StyleProp; +} + +const ARROW_SIZE = 32; +const ARROW_BEVEL = 4; +const ARROW_BORDER = 2; + +/** + * DT-styled Gallery with thumbnail strip and arrow navigation + * + * @example + * + */ +export function DTGallery({ + items, + activeIndex, + onSelect, + variant = 'normal', + thumbnailSize = 64, + bevelSize = 24, + borderWidth = 3, + style, +}: DTGalleryProps) { + const flatListRef = useRef(null); + const {dimensions, onLayout, hasDimensions} = useComponentLayout(); + const accentColor = getVariantColor(variant); + const atStart = activeIndex <= 0; + const atEnd = activeIndex >= items.length - 1; + + const handlePrev = useCallback(() => { + if (activeIndex > 0) { + onSelect(activeIndex - 1); + } + }, [activeIndex, onSelect]); + + const handleNext = useCallback(() => { + if (activeIndex < items.length - 1) { + onSelect(activeIndex + 1); + } + }, [activeIndex, items.length, onSelect]); + + const scrollToActive = useCallback( + (index: number) => { + flatListRef.current?.scrollToIndex({ + index, + animated: true, + viewPosition: 0.5, + }); + }, + [], + ); + + const handleSelect = useCallback( + (index: number) => { + onSelect(index); + scrollToActive(index); + }, + [onSelect, scrollToActive], + ); + + const getItemLayout = useCallback( + (_: unknown, index: number) => ({ + length: thumbnailSize + 8, + offset: (thumbnailSize + 8) * index, + index, + }), + [thumbnailSize], + ); + + const renderArrow = (direction: 'left' | 'right', onPress: () => void, isDisabled: boolean) => { + const center = ARROW_SIZE / 2; + const arrowSize = ARROW_SIZE * 0.2; + const iconPath = + direction === 'left' + ? `M ${center + arrowSize} ${center - arrowSize} L ${center - arrowSize} ${center} L ${center + arrowSize} ${center + arrowSize}` + : `M ${center - arrowSize} ${center - arrowSize} L ${center + arrowSize} ${center} L ${center - arrowSize} ${center + arrowSize}`; + + return ( + ({ + opacity: isDisabled ? 0.3 : pressed ? 0.7 : 1, + })}> + + + + + + ); + }; + + const renderThumbnail = ({item, index}: {item: DTGalleryItem; index: number}) => { + const isActive = index === activeIndex; + return ( + handleSelect(index)} + style={({pressed}) => ({opacity: pressed ? 0.7 : 1})}> + + {/* Android glow: colored border overlay since elevation is gray-only */} + {isActive && Platform.OS === 'android' && ( + + )} + + + + ); + }; + + if (items.length === 0) return null; + + const {width, height} = dimensions; + + // Outer and inner bevel paths for frame overlay + const outerPath = hasDimensions + ? buildMediaFrameBevelPath(width, height, bevelSize) + : ''; + const innerPath = hasDimensions + ? buildMediaFrameBevelPath(width, height, bevelSize - borderWidth, borderWidth) + : ''; + + return ( + + {/* Main image with beveled frame */} + + {/* Background fill */} + {hasDimensions && ( + + + + )} + + + + {/* Frame overlay (above content) — clips image at beveled border */} + {hasDimensions && ( + + {/* Corner mask: fill area outside outer bevel with dark to hide overflow */} + + {/* Colored border between outer and inner bevel paths */} + + + )} + + + {/* Thumbnail strip with arrows */} + {items.length > 1 && ( + + {renderArrow('left', handlePrev, atStart)} + item.id} + horizontal + showsHorizontalScrollIndicator={false} + getItemLayout={getItemLayout} + contentContainerStyle={styles.thumbnailList} + style={styles.thumbnailFlatList} + initialNumToRender={8} + maxToRenderPerBatch={4} + windowSize={5} + removeClippedSubviews={Platform.OS === 'android'} + /> + {renderArrow('right', handleNext, atEnd)} + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + gap: 12, + }, + mainImageContainer: { + aspectRatio: 1, + position: 'relative', + }, + mainImageContent: { + position: 'relative', + zIndex: 1, + flex: 1, + overflow: 'hidden', + }, + mainImage: { + flex: 1, + }, + thumbnailStrip: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + thumbnailFlatList: { + flex: 1, + }, + thumbnailList: { + gap: 8, + }, + thumbnail: { + borderWidth: 2, + overflow: 'hidden', + }, + thumbnailActive: { + ...Platform.select({ + ios: { + shadowOffset: {width: 0, height: 0}, + shadowOpacity: 0.8, + shadowRadius: 8, + }, + }), + }, + thumbnailImage: { + flex: 1, + }, + frameOverlay: { + zIndex: 2, + }, +}); diff --git a/packages/react-native/src/components/DTHexagon.tsx b/packages/react-native/src/components/DTHexagon.tsx new file mode 100644 index 0000000..fe9b817 --- /dev/null +++ b/packages/react-native/src/components/DTHexagon.tsx @@ -0,0 +1,210 @@ +/** + * DT Hexagon Component + * + * Decorative hexagon shape following the Dangerous Things design language. + * No RNP equivalent — purely SVG-based. + * + * Web CSS reference (AnimatedHexagon / HexagonHamburgerMenu): + * - Flat-top hexagon shape + * - Optional rotation and pulse animations + * - Used for loading indicators, backgrounds, menu icons + */ + +import {useRef, useEffect} from 'react'; +import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native'; +import Svg, {Polygon} from 'react-native-svg'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +interface DTHexagonProps { + /** + * Hexagon diameter in pixels + */ + size: number; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Whether the hexagon has a solid fill + * @default true + */ + filled?: boolean; + /** + * Whether to animate + * @default false + */ + animated?: boolean; + /** + * Animation type + * @default 'none' + */ + animationType?: 'rotate' | 'pulse' | 'none'; + /** + * Animation duration in milliseconds + * @default 2000 + */ + animationDuration?: number; + /** + * Stroke/border width when not filled + * @default 2 + */ + borderWidth?: number; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Opacity + * @default 1 + */ + opacity?: number; + /** + * Additional styles + */ + style?: StyleProp; + /** + * Content rendered centered inside the hexagon + */ + children?: React.ReactNode; +} + +/** + * Build flat-top hexagon SVG polygon points + */ +function buildHexagonPoints(size: number): string { + const cx = size / 2; + const cy = size / 2; + const r = size / 2; + + return [0, 1, 2, 3, 4, 5] + .map(i => { + const angle = (Math.PI / 3) * i - Math.PI / 6; // flat-top orientation + const x = cx + r * Math.cos(angle); + const y = cy + r * Math.sin(angle); + return `${x},${y}`; + }) + .join(' '); +} + +/** + * DT-styled decorative Hexagon + * + * @example + * + * + * @example + * + * + * @example + * + */ +export function DTHexagon({ + size, + variant = 'normal', + filled = true, + animated = false, + animationType = 'none', + animationDuration = 2000, + borderWidth = 2, + color, + opacity: opacityProp = 1, + style, + children, +}: DTHexagonProps) { + const accentColor = getVariantColor(variant, color); + const rotateAnim = useRef(new Animated.Value(0)).current; + const pulseAnim = useRef(new Animated.Value(1)).current; + + useEffect(() => { + if (!animated) { + rotateAnim.setValue(0); + pulseAnim.setValue(1); + return; + } + + if (animationType === 'rotate') { + const animation = Animated.loop( + Animated.timing(rotateAnim, { + toValue: 1, + duration: animationDuration, + useNativeDriver: true, + }), + ); + animation.start(); + return () => { + animation.stop(); + rotateAnim.setValue(0); + }; + } + + if (animationType === 'pulse') { + const animation = Animated.loop( + Animated.sequence([ + Animated.timing(pulseAnim, { + toValue: 0.3, + duration: animationDuration / 2, + useNativeDriver: true, + }), + Animated.timing(pulseAnim, { + toValue: 1, + duration: animationDuration / 2, + useNativeDriver: true, + }), + ]), + ); + animation.start(); + return () => { + animation.stop(); + pulseAnim.setValue(1); + }; + } + }, [animated, animationType, animationDuration, rotateAnim, pulseAnim]); + + const rotation = rotateAnim.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '360deg'], + }); + + const animatedOpacity = + animated && animationType === 'pulse' ? pulseAnim : opacityProp; + + const transform = + animated && animationType === 'rotate' + ? [{rotate: rotation}] + : []; + + const points = buildHexagonPoints(size); + + return ( + 0 ? {transform} : undefined, + style, + ]}> + + + + {children && ( + + {children} + + )} + + ); +} + +const styles = StyleSheet.create({ + container: {}, + childrenContainer: { + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/packages/react-native/src/components/DTLabel.tsx b/packages/react-native/src/components/DTLabel.tsx new file mode 100644 index 0000000..583e8cb --- /dev/null +++ b/packages/react-native/src/components/DTLabel.tsx @@ -0,0 +1,313 @@ +/** + * DT Label Component + * + * A themed label following the Dangerous Things design language + * with an SVG-based beveled top-right corner. + * + * Web CSS reference (CyberLabel): + * - Top-right bevel: 1em (~16px) + * - Solid filled background using mode color + * - Black text on colored background + */ + +import {ReactNode, useEffect, useRef} from 'react'; +import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native'; +import {Text} from 'react-native-paper'; +import Svg, {Path} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildLabelBevelPath} from '../utils/bevelPaths'; +import {useComponentLayout} from '../utils/useComponentLayout'; + +type DTLabelSize = 'small' | 'medium' | 'large'; + +interface SizeConfig { + fontSize: number; + fontSizeSecondary: number; + paddingHorizontal: number; + paddingVertical: number; + bevel: number; + indicatorWidth: number; + gap: number; +} + +const sizeConfigs: Record = { + small: { + fontSize: 11, + fontSizeSecondary: 11, + paddingHorizontal: 8, + paddingVertical: 4, + bevel: 10, + indicatorWidth: 12, + gap: 3, + }, + medium: { + fontSize: 14, + fontSizeSecondary: 14, + paddingHorizontal: 12, + paddingVertical: 8, + bevel: 16, + indicatorWidth: 16, + gap: 4, + }, + large: { + fontSize: 18, + fontSizeSecondary: 18, + paddingHorizontal: 16, + paddingVertical: 12, + bevel: 22, + indicatorWidth: 20, + gap: 6, + }, +}; + +interface DTLabelProps { + /** + * Primary text content + */ + primaryText: ReactNode; + /** + * Secondary text (displayed bold, after primary) + */ + secondaryText?: ReactNode; + /** + * Color mode for the label + * @default 'normal' + */ + mode?: DTVariant; + /** + * Size of the label - affects text size and scales all dimensions + * @default 'medium' + */ + size?: DTLabelSize; + /** + * Whether the label should fill its container width + * @default false + */ + fullWidth?: boolean; + /** + * Whether to animate the label on mount + * @default true + */ + animated?: boolean; + /** + * Animation delay in milliseconds + * @default 100 + */ + animationDelay?: number; + /** + * Animation duration in milliseconds + * @default 330 + */ + animationDuration?: number; + /** + * Whether to show the pinging indicator + * @default true + */ + showIndicator?: boolean; + /** + * Top-right bevel size in pixels (overrides size-based default) + */ + bevelSize?: number; + /** + * Additional styles for the label container + */ + style?: StyleProp; +} + +/** + * DT-styled Label component with SVG beveled top-right corner + * + * Based on CyberLabel from the Dangerous Things web storefront. + * Features a solid colored background with black text and an optional + * animated ping indicator. + * + * @example + * + * + * @example + * + * + * @example + * + * + * @example + * + * + * @example + * + */ +export function DTLabel({ + primaryText, + secondaryText, + mode = 'normal', + size = 'medium', + fullWidth = false, + animated = true, + animationDelay = 100, + animationDuration = 330, + showIndicator = true, + bevelSize, + style, +}: DTLabelProps) { + const {dimensions, onLayout, hasDimensions} = useComponentLayout(); + const scaleAnim = useRef(new Animated.Value(animated ? 0 : 1)).current; + const pingAnim = useRef(new Animated.Value(1)).current; + const backgroundColor = getVariantColor(mode); + const sizeConfig = sizeConfigs[size]; + const effectiveBevelSize = bevelSize ?? sizeConfig.bevel; + + // Mount animation + useEffect(() => { + if (animated) { + Animated.timing(scaleAnim, { + toValue: 1, + duration: animationDuration, + delay: animationDelay, + useNativeDriver: true, + }).start(); + } + }, [animated, animationDelay, animationDuration, scaleAnim]); + + // Ping animation loop + useEffect(() => { + if (showIndicator) { + const pingAnimation = Animated.loop( + Animated.sequence([ + Animated.timing(pingAnim, { + toValue: 0.3, + duration: 1000, + useNativeDriver: true, + }), + Animated.timing(pingAnim, { + toValue: 1, + duration: 1000, + useNativeDriver: true, + }), + ]), + ); + pingAnimation.start(); + return () => { + pingAnimation.stop(); + pingAnim.setValue(1); + }; + } + pingAnim.setValue(1); + }, [showIndicator, pingAnim]); + + const {width, height} = dimensions; + + return ( + + {hasDimensions && ( + + + + )} + + + + {primaryText} + + {secondaryText && ( + + {secondaryText} + + )} + + {showIndicator && ( + + + + )} + + + ); +} + +/** + * DT Label design constants matching web CSS + */ +export const DTLabelClipPath = { + // CSS-style clip path (for web reference) + css: `polygon( + 0% 0%, + calc(100% - 1em) 0%, + 100% 1em, + 100% 100%, + 0% 100% + )`, + // Default bevel size in pixels (matching 1em at 16px base) + bevelSize: 16, +}; + +const styles = StyleSheet.create({ + container: { + position: 'relative', + alignSelf: 'flex-start', + margin: 8, + }, + fullWidth: { + alignSelf: 'stretch', + }, + innerContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + position: 'relative', + zIndex: 1, + }, + textContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + alignItems: 'center', + }, + primaryText: { + color: DTColors.dark, + fontWeight: '500', + }, + secondaryText: { + color: DTColors.dark, + fontWeight: '800', + }, + indicator: {}, + indicatorLine: { + borderBottomWidth: 2, + }, +}); diff --git a/packages/react-native/src/components/DTMediaFrame.tsx b/packages/react-native/src/components/DTMediaFrame.tsx new file mode 100644 index 0000000..af399f5 --- /dev/null +++ b/packages/react-native/src/components/DTMediaFrame.tsx @@ -0,0 +1,210 @@ +/** + * DT MediaFrame Component + * + * A beveled frame for images and video content following the + * Dangerous Things design language. No RNP equivalent. + * + * Web CSS reference (CyberMediaFrame / .media-frame-clipped): + * - Top-left + bottom-right diagonal symmetry bevels + * - Colored border with dark inner background + * - Optional mount scale animation + */ + +import {ReactNode, useRef, useEffect} from 'react'; +import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native'; +import Svg, {Path, Defs, ClipPath} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildMediaFrameBevelPath} from '../utils/bevelPaths'; +import {useComponentLayout} from '../utils/useComponentLayout'; + +interface DTMediaFrameProps { + /** + * Content to frame (Image, Video, etc.) + */ + children: ReactNode; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Aspect ratio (e.g., 16/9, 1, 4/3) + */ + aspectRatio?: number; + /** + * Border width in pixels + * @default 3 + */ + borderWidth?: number; + /** + * Bevel size in pixels (top-left and bottom-right) + * @default 32 + */ + bevelSize?: number; + /** + * Whether to animate on mount + * @default true + */ + animated?: boolean; + /** + * Animation delay in milliseconds + * @default 0 + */ + animationDelay?: number; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Additional styles for outer container + */ + style?: StyleProp; + /** + * Additional styles for the content area + */ + contentStyle?: StyleProp; +} + +/** + * DT-styled MediaFrame with diagonal-symmetry beveled corners + * + * @example + * + * + * + * + * @example + * + * + */ +export function DTMediaFrame({ + children, + variant = 'normal', + aspectRatio, + borderWidth = 3, + bevelSize = 32, + animated = true, + animationDelay = 0, + color, + style, + contentStyle, +}: DTMediaFrameProps) { + const {dimensions, onLayout, hasDimensions} = useComponentLayout(); + const scaleAnim = useRef(new Animated.Value(animated ? 0 : 1)).current; + const accentColor = getVariantColor(variant, color); + + useEffect(() => { + if (animated) { + Animated.timing(scaleAnim, { + toValue: 1, + duration: 300, + delay: animationDelay, + useNativeDriver: true, + }).start(); + } + }, [animated, animationDelay, scaleAnim]); + + const {width, height} = dimensions; + + // Outer bevel path (full frame shape) + const outerPath = hasDimensions + ? buildMediaFrameBevelPath(width, height, bevelSize) + : ''; + // Inner bevel path at border inset + const innerPath = hasDimensions + ? buildMediaFrameBevelPath(width, height, bevelSize - borderWidth, borderWidth) + : ''; + + return ( + + {/* Background fill (behind content) */} + {hasDimensions && ( + + + + )} + {/* Content clipped to the inner bevel shape */} + {hasDimensions ? ( + + + + + + + + + + + ) : ( + + {children} + + )} + {/* Frame overlay (above content) — masks content at beveled corners */} + {hasDimensions && ( + + {/* Outer frame border */} + + {/* Corner masks: fill the rectangular area outside the bevel shape with black + to hide content that overflows past the beveled corners */} + + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + position: 'relative', + }, + content: { + position: 'relative', + zIndex: 1, + overflow: 'hidden', + flex: 1, + }, + frameOverlay: { + zIndex: 2, + }, +}); diff --git a/packages/react-native/src/components/DTMenu.tsx b/packages/react-native/src/components/DTMenu.tsx new file mode 100644 index 0000000..031280d --- /dev/null +++ b/packages/react-native/src/components/DTMenu.tsx @@ -0,0 +1,371 @@ +/** + * DT Menu Component + * + * Wraps React Native Paper's Menu for dropdown positioning, with custom + * menu items styled to the Dangerous Things aesthetic. + * + * Web CSS reference (CyberMenu): + * - Hierarchical items with menu-item-clipped styling + * - Active/selected state with emphasis color fill + * - borderTopWidth: 5 thick accent border + */ + +import {useState, useCallback, useRef, useEffect} from 'react'; +import { + StyleSheet, + View, + ViewStyle, + StyleProp, + Pressable, + Animated, +} from 'react-native'; +import {Menu, Text} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +export interface DTMenuItem { + /** + * Unique identifier + */ + id: string; + /** + * Display title + */ + title: string; + /** + * Press handler (leaf items) + */ + onPress?: () => void; + /** + * Sub-menu items + */ + items?: DTMenuItem[]; + /** + * Whether this item is currently active + */ + isActive?: boolean; +} + +interface DTMenuProps { + /** + * Menu items + */ + items: DTMenuItem[]; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Variant for active items + * @default 'emphasis' + */ + activeVariant?: DTVariant; + /** + * Layout direction + * @default 'vertical' + */ + layout?: 'horizontal' | 'vertical'; + /** + * Additional styles + */ + style?: StyleProp; +} + +/** + * DT-styled Menu with hierarchical items + * + * For inline menu display (not dropdown). Items with sub-items + * expand/collapse on press. + * + * @example + * + */ +export function DTMenu({ + items, + variant = 'normal', + activeVariant = 'emphasis', + layout = 'vertical', + style, +}: DTMenuProps) { + return ( + + {items.map(item => ( + + ))} + + ); +} + +/** + * For dropdown use, wraps RNP Menu for positioning/portal behavior + */ +interface DTMenuDropdownProps { + /** + * Menu items + */ + items: DTMenuItem[]; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Anchor element or position + */ + anchor: React.ReactNode; + /** + * Whether the menu is visible + */ + visible: boolean; + /** + * Called when the menu is dismissed + */ + onDismiss: () => void; + /** + * Additional styles + */ + style?: StyleProp; +} + +/** + * DT-styled dropdown Menu wrapping React Native Paper Menu + * + * @example + * Open Menu} + * visible={menuVisible} + * onDismiss={() => setMenuVisible(false)} + * /> + */ +export function DTMenuDropdown({ + items, + variant = 'normal', + anchor, + visible, + onDismiss, + style, +}: DTMenuDropdownProps) { + const accentColor = getVariantColor(variant); + + return ( + + {items.map(item => ( + { + item.onPress?.(); + onDismiss(); + }} + titleStyle={[styles.dropdownItemTitle, {color: DTColors.light}]} + style={styles.dropdownItem} + /> + ))} + + ); +} + +// --- Internal DTMenuItemRow --- + +function DTMenuItemRow({ + item, + variant, + activeVariant, + level, +}: { + item: DTMenuItem; + variant: DTVariant; + activeVariant: DTVariant; + level: number; +}) { + const [expanded, setExpanded] = useState(false); + const hasChildren = item.items && item.items.length > 0; + const isActive = item.isActive || expanded; + const itemColor = getVariantColor(isActive ? activeVariant : variant); + + const heightAnim = useRef(new Animated.Value(0)).current; + const chevronAnim = useRef(new Animated.Value(0)).current; + const [contentHeight, setContentHeight] = useState(0); + const [measured, setMeasured] = useState(false); + + useEffect(() => { + if (!hasChildren) return; + Animated.parallel([ + Animated.timing(heightAnim, { + toValue: expanded ? 1 : 0, + duration: 250, + useNativeDriver: false, + }), + Animated.timing(chevronAnim, { + toValue: expanded ? 1 : 0, + duration: 250, + useNativeDriver: true, + }), + ]).start(); + }, [expanded, heightAnim, chevronAnim, hasChildren]); + + const handlePress = useCallback(() => { + if (hasChildren) { + setExpanded(prev => !prev); + } else { + item.onPress?.(); + } + }, [hasChildren, item]); + + const animatedHeight = heightAnim.interpolate({ + inputRange: [0, 1], + outputRange: [0, contentHeight], + }); + + const chevronRotate = chevronAnim.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '180deg'], + }); + + return ( + + [ + styles.menuItem, + { + borderColor: itemColor, + borderTopColor: itemColor, + backgroundColor: isActive + ? `${itemColor}20` + : DTColors.dark, + paddingLeft: 16 + level * 16, + opacity: pressed ? 0.7 : 1, + }, + ]}> + + {item.title} + + {hasChildren && ( + + + + )} + + {hasChildren && ( + + { + const h = e.nativeEvent.layout.height; + if (h > 0 && !measured) { + setContentHeight(h); + setMeasured(true); + if (!expanded) { + heightAnim.setValue(0); + } + } + }}> + {item.items?.map(child => ( + + ))} + + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + gap: 4, + }, + horizontal: { + flexDirection: 'row', + flexWrap: 'wrap', + }, + vertical: { + flexDirection: 'column', + }, + menuItem: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderWidth: 1, + borderTopWidth: 5, + paddingVertical: 12, + paddingRight: 16, + }, + menuItemTitle: { + fontWeight: '600', + letterSpacing: 0.5, + textTransform: 'uppercase', + fontSize: 14, + }, + chevron: { + justifyContent: 'center', + alignItems: 'center', + width: 24, + height: 24, + }, + chevronArrow: { + width: 0, + height: 0, + borderLeftWidth: 5, + borderRightWidth: 5, + borderTopWidth: 7, + borderLeftColor: 'transparent', + borderRightColor: 'transparent', + }, + dropdownContent: { + borderWidth: 2, + borderRadius: 0, + backgroundColor: DTColors.dark, + }, + dropdownItem: { + borderRadius: 0, + }, + dropdownItemTitle: { + letterSpacing: 0.3, + }, +}); diff --git a/packages/react-native/src/components/DTModal.tsx b/packages/react-native/src/components/DTModal.tsx new file mode 100644 index 0000000..0c33e73 --- /dev/null +++ b/packages/react-native/src/components/DTModal.tsx @@ -0,0 +1,115 @@ +/** + * DT Modal Component + * + * Wraps React Native Paper's Modal + Portal with the Dangerous Things + * aesthetic. Content is rendered inside a DTCard for the beveled look. + * + * Web CSS reference (Modal.tsx): + * - Centered overlay with dark backdrop + * - Content in beveled card container + */ + +import {StyleSheet, ViewStyle, StyleProp, KeyboardAvoidingView, Platform} from 'react-native'; +import {Portal, Modal} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant} from '../utils/variantColors'; +import {DTCard} from './DTCard'; + +interface DTModalProps { + /** + * Whether the modal is visible + */ + visible: boolean; + /** + * Called when the modal is dismissed + */ + onDismiss: () => void; + /** + * Optional title for the modal header + */ + title?: string; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Modal content + */ + children: React.ReactNode; + /** + * Whether the modal can be dismissed by tapping the backdrop + * @default true + */ + dismissable?: boolean; + /** + * Additional styles for the card content area + */ + contentStyle?: StyleProp; + /** + * Additional styles for the modal container + */ + style?: StyleProp; +} + +/** + * DT-styled Modal wrapping React Native Paper Modal + Portal + * + * @example + * setShowModal(false)} title="Confirm"> + * Are you sure? + * + * + * @example + * + * Something went wrong + * + */ +export function DTModal({ + visible, + onDismiss, + title, + variant = 'normal', + children, + dismissable = true, + contentStyle, + style, +}: DTModalProps) { + return ( + + + + + {children} + + + + + ); +} + +const styles = StyleSheet.create({ + modal: {}, + container: { + marginHorizontal: 24, + }, + keyboardAvoid: { + width: '100%', + }, +}); diff --git a/packages/react-native/src/components/DTProgressBar.tsx b/packages/react-native/src/components/DTProgressBar.tsx new file mode 100644 index 0000000..c1ce698 --- /dev/null +++ b/packages/react-native/src/components/DTProgressBar.tsx @@ -0,0 +1,183 @@ +/** + * DT ProgressBar Component + * + * A themed progress bar wrapping React Native Paper's ProgressBar + * with the Dangerous Things angular aesthetic. + * + * Web CSS reference (.progress / .progress-indicator): + * - Vertical or horizontal bar showing progress 0-100% + * - Mode-colored fill + */ + +import {StyleSheet, View, ViewStyle, StyleProp} from 'react-native'; +import {ProgressBar, Text} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {useComponentLayout} from '../utils/useComponentLayout'; + +interface DTProgressBarProps { + /** + * Progress value between 0 and 1 + */ + value: number; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Orientation of the bar + * @default 'horizontal' + */ + orientation?: 'horizontal' | 'vertical'; + /** + * Bar thickness in pixels + * @default 4 + */ + height?: number; + /** + * Whether to animate progress changes + * @default true + */ + animated?: boolean; + /** + * Show percentage label + * @default false + */ + showLabel?: boolean; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Additional styles + */ + style?: StyleProp; +} + +/** + * DT-styled ProgressBar wrapping React Native Paper ProgressBar + * + * @example + * + * + * @example + * + * + * @example + * + */ +export function DTProgressBar({ + value, + variant = 'normal', + orientation = 'horizontal', + height = 4, + animated: _animated = true, + showLabel = false, + color, + style, +}: DTProgressBarProps) { + const accentColor = getVariantColor(variant, color); + const clampedValue = Math.max(0, Math.min(1, value)); + + if (orientation === 'vertical') { + return ( + + ); + } + + return ( + + + {showLabel && ( + + {Math.round(clampedValue * 100)}% + + )} + + ); +} + +/** + * Vertical progress bar using measured pixel height instead of + * percentage strings (which don't animate smoothly in RN). + */ +function VerticalProgressBar({ + value, + color, + width, + showLabel, + style, +}: { + value: number; + color: string; + width: number; + showLabel: boolean; + style?: StyleProp; +}) { + const {dimensions, onLayout, hasDimensions} = useComponentLayout(); + const fillHeight = hasDimensions ? dimensions.height * value : 0; + + return ( + + + + + {showLabel && ( + + {Math.round(value * 100)}% + + )} + + ); +} + +const styles = StyleSheet.create({ + horizontalContainer: { + gap: 8, + }, + bar: { + borderRadius: 0, + }, + verticalContainer: { + alignItems: 'center', + gap: 8, + }, + verticalTrack: { + flex: 1, + width: '100%', + backgroundColor: DTColors.disabledBackground, + justifyContent: 'flex-end', + }, + verticalFill: { + width: '100%', + }, + label: { + fontSize: 11, + fontWeight: '600', + letterSpacing: 0.5, + }, +}); diff --git a/packages/react-native/src/components/DTQuantityStepper.tsx b/packages/react-native/src/components/DTQuantityStepper.tsx new file mode 100644 index 0000000..c58f213 --- /dev/null +++ b/packages/react-native/src/components/DTQuantityStepper.tsx @@ -0,0 +1,234 @@ +/** + * DT QuantityStepper Component + * + * Custom-built quantity increment/decrement control following the + * Dangerous Things design language. No RNP equivalent exists. + * + * Web CSS reference (AddOnOptionItem quantity controls): + * - Plus/minus buttons with beveled corners + * - Quantity display between buttons + */ + +import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native'; +import {Text} from 'react-native-paper'; +import Svg, {Path} from 'react-native-svg'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; +import {buildButtonBevelPath} from '../utils/bevelPaths'; + +type DTStepperSize = 'small' | 'medium' | 'large'; + +interface SizeConfig { + buttonSize: number; + fontSize: number; + bevelSize: number; + iconStrokeWidth: number; + minWidth: number; +} + +const sizeConfigs: Record = { + small: { + buttonSize: 28, + fontSize: 12, + bevelSize: 4, + iconStrokeWidth: 1.5, + minWidth: 28, + }, + medium: { + buttonSize: 36, + fontSize: 16, + bevelSize: 6, + iconStrokeWidth: 2, + minWidth: 36, + }, + large: { + buttonSize: 44, + fontSize: 20, + bevelSize: 8, + iconStrokeWidth: 2.5, + minWidth: 44, + }, +}; + +interface DTQuantityStepperProps { + /** + * Current quantity value + */ + value: number; + /** + * Called when value changes + */ + onValueChange: (value: number) => void; + /** + * Minimum value + * @default 0 + */ + min?: number; + /** + * Maximum value + * @default 99 + */ + max?: number; + /** + * Step increment + * @default 1 + */ + step?: number; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Whether the stepper is disabled + */ + disabled?: boolean; + /** + * Label text displayed above the stepper + */ + label?: string; + /** + * Size preset + * @default 'medium' + */ + size?: DTStepperSize; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Additional styles + */ + style?: StyleProp; +} + +/** + * DT-styled QuantityStepper with beveled +/- buttons + * + * @example + * + * + * @example + * + */ +export function DTQuantityStepper({ + value, + onValueChange, + min = 0, + max = 99, + step = 1, + variant = 'normal', + disabled = false, + label, + size = 'medium', + color, + style, +}: DTQuantityStepperProps) { + const accentColor = getVariantColor(variant, color); + const config = sizeConfigs[size]; + const atMin = value <= min; + const atMax = value >= max; + const borderWidth = 2; + + const handleDecrement = () => { + const next = Math.max(min, value - step); + onValueChange(next); + }; + + const handleIncrement = () => { + const next = Math.min(max, value + step); + onValueChange(next); + }; + + const renderStepButton = ( + type: 'minus' | 'plus', + onPress: () => void, + isDisabled: boolean, + ) => { + const s = config.buttonSize; + const center = s / 2; + const iconSize = s * 0.35; + const sw = config.iconStrokeWidth; + + // Minus: horizontal line; Plus: cross + const iconPath = + type === 'minus' + ? `M ${center - iconSize} ${center} L ${center + iconSize} ${center}` + : `M ${center - iconSize} ${center} L ${center + iconSize} ${center} M ${center} ${center - iconSize} L ${center} ${center + iconSize}`; + + // Expand touch target to at least 44px + const hitSlopSize = Math.max(0, (44 - s) / 2); + const hitSlop = hitSlopSize > 0 ? hitSlopSize : undefined; + + return ( + ({ + opacity: disabled || isDisabled ? 0.3 : pressed ? 0.7 : 1, + })}> + + + + + + ); + }; + + return ( + + {label && ( + {label} + )} + + {renderStepButton('minus', handleDecrement, atMin)} + + + {value} + + + {renderStepButton('plus', handleIncrement, atMax)} + + + ); +} + +const styles = StyleSheet.create({ + wrapper: { + gap: 8, + }, + container: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + label: { + fontSize: 14, + letterSpacing: 0.3, + }, + valueContainer: { + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 8, + }, + valueText: { + fontWeight: '700', + letterSpacing: 0.5, + }, +}); diff --git a/packages/react-native/src/components/DTRadioGroup.tsx b/packages/react-native/src/components/DTRadioGroup.tsx new file mode 100644 index 0000000..2835d2a --- /dev/null +++ b/packages/react-native/src/components/DTRadioGroup.tsx @@ -0,0 +1,229 @@ +/** + * DT RadioGroup + RadioOption Components + * + * Custom-built angular radio selection following the Dangerous Things design language. + * RNP RadioButton uses circular geometry with animated SVG circles, + * so we build hexagonal indicators from scratch. + * + * Web CSS reference (AddOnSelector single-select): + * - Selected: border highlight + semi-transparent fill (var(--selected) at 70% opacity) + * - Hexagon indicator instead of circular radio button + */ + +import {createContext, useContext} from 'react'; +import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native'; +import {Text} from 'react-native-paper'; +import Svg, {Polygon} from 'react-native-svg'; +import {DTColors, getColor} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +// --- Context --- + +interface RadioContextValue { + value: string | null; + onValueChange: (value: string) => void; + variant: DTVariant; +} + +const RadioContext = createContext(null); + +// --- DTRadioGroup --- + +interface DTRadioGroupProps { + /** + * Currently selected value + */ + value: string | null; + /** + * Called when selection changes + */ + onValueChange: (value: string) => void; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Radio option children + */ + children: React.ReactNode; + /** + * Additional styles for the container + */ + style?: StyleProp; +} + +/** + * DT-styled RadioGroup container + * + * @example + * + * + * + * + */ +export function DTRadioGroup({ + value, + onValueChange, + variant = 'normal', + children, + style, +}: DTRadioGroupProps) { + return ( + + {children} + + ); +} + +// --- DTRadioOption --- + +interface DTRadioOptionProps { + /** + * Value identifier for this option + */ + value: string; + /** + * Label text + */ + label: string; + /** + * Optional description text + */ + description?: string; + /** + * Whether this option is disabled + */ + disabled?: boolean; + /** + * Additional styles + */ + style?: StyleProp; +} + +const INDICATOR_SIZE = 22; +const INDICATOR_INNER = 12; +const INDICATOR_BORDER = 2; + +/** Generate flat-top hexagon points string for SVG Polygon */ +function hexPoints(cx: number, cy: number, r: number): string { + const pts: string[] = []; + for (let i = 0; i < 6; i++) { + const angle = (Math.PI / 3) * i - Math.PI / 6; // flat-top: start at -30° + pts.push(`${cx + r * Math.cos(angle)},${cy + r * Math.sin(angle)}`); + } + return pts.join(' '); +} + +/** + * DT-styled RadioOption with hexagonal indicator + * + * Must be used inside a DTRadioGroup. + * + * @example + * + */ +export function DTRadioOption({ + value, + label, + description, + disabled = false, + style, +}: DTRadioOptionProps) { + const context = useContext(RadioContext); + if (!context) { + throw new Error('DTRadioOption must be used inside DTRadioGroup'); + } + + const {value: selectedValue, onValueChange, variant} = context; + const isSelected = selectedValue === value; + const accentColor = getVariantColor(variant); + const selectedBgColor = getColor( + variant === 'normal' + ? 'modeNormalSelected' + : variant === 'emphasis' + ? 'modeEmphasisSelected' + : 'modeNormal', + ); + const opacity = disabled ? 0.5 : 1; + + return ( + !disabled && onValueChange(value)} + style={({pressed}) => [ + styles.option, + { + borderColor: isSelected ? accentColor : getColor('modeNormal', 0.3), + backgroundColor: isSelected ? selectedBgColor : 'transparent', + opacity: pressed ? 0.7 : opacity, + }, + style, + ]}> + {/* Hexagon indicator */} + + + {isSelected && ( + + )} + + {/* Text content */} + + + {label} + + {description && ( + + {description} + + )} + + + ); +} + +const styles = StyleSheet.create({ + group: { + gap: 8, + }, + option: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + padding: 12, + borderWidth: 2, + }, + optionText: { + flex: 1, + gap: 2, + }, + optionLabel: { + fontSize: 14, + fontWeight: '500', + letterSpacing: 0.3, + }, + optionDescription: { + fontSize: 12, + letterSpacing: 0.2, + }, +}); diff --git a/packages/react-native/src/components/DTSearchInput.tsx b/packages/react-native/src/components/DTSearchInput.tsx new file mode 100644 index 0000000..14797c3 --- /dev/null +++ b/packages/react-native/src/components/DTSearchInput.tsx @@ -0,0 +1,163 @@ +/** + * DT SearchInput Component + * + * A themed search input wrapping React Native Paper's Searchbar + * with the Dangerous Things angular aesthetic. + * + * Styled input only — predictive results logic is app-specific + * (composable principle from CLAUDE.md). + */ + +import {useState, useRef, useEffect} from 'react'; +import {StyleSheet, View, ViewStyle, TextStyle, StyleProp, Animated} from 'react-native'; +import {Searchbar, ActivityIndicator} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +interface DTSearchInputProps { + /** + * Current search text + */ + value: string; + /** + * Called when text changes + */ + onChangeText: (text: string) => void; + /** + * Called when search is submitted + */ + onSubmit?: () => void; + /** + * Placeholder text + * @default 'Search...' + */ + placeholder?: string; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Show loading indicator instead of search icon + * @default false + */ + loading?: boolean; + /** + * Whether the input is disabled + */ + disabled?: boolean; + /** + * Auto-focus the input on mount + */ + autoFocus?: boolean; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Additional styles for the container + */ + style?: StyleProp; + /** + * Additional styles for the input text + */ + inputStyle?: StyleProp; +} + +/** + * DT-styled SearchInput wrapping React Native Paper Searchbar + * + * @example + * + * + * @example + * + */ +export function DTSearchInput({ + value, + onChangeText, + onSubmit, + placeholder = 'Search...', + variant = 'normal', + loading = false, + disabled = false, + autoFocus = false, + color, + style, + inputStyle, +}: DTSearchInputProps) { + const accentColor = getVariantColor(variant, color); + const [focused, setFocused] = useState(false); + const focusAnim = useRef(new Animated.Value(0)).current; + + useEffect(() => { + const anim = Animated.timing(focusAnim, { + toValue: focused ? 1 : 0, + duration: 150, + useNativeDriver: false, + }); + anim.start(); + return () => anim.stop(); + }, [focused, focusAnim]); + + return ( + + setFocused(true)} + onBlur={() => setFocused(false)} + returnKeyType="search" + placeholder={placeholder} + placeholderTextColor={DTColors.disabled} + editable={!disabled} + autoFocus={autoFocus} + icon={ + loading + ? () => + : 'magnify' + } + iconColor={accentColor} + inputStyle={[styles.input, {color: DTColors.light}, inputStyle]} + style={[styles.searchbar, {borderColor: accentColor}]} + rippleColor={accentColor} + theme={{ + colors: { + elevation: {level3: DTColors.dark}, + onSurface: DTColors.light, + onSurfaceVariant: accentColor, + }, + roundness: 0, + }} + /> + {/* Focus glow bar — matches DTTextInput */} + + + ); +} + +const styles = StyleSheet.create({ + container: {}, + searchbar: { + borderWidth: 2, + backgroundColor: DTColors.dark, + elevation: 0, + }, + input: { + fontSize: 14, + letterSpacing: 0.3, + }, + focusBar: { + height: 4, + marginTop: 1, + }, +}); diff --git a/packages/react-native/src/components/DTSwitch.tsx b/packages/react-native/src/components/DTSwitch.tsx new file mode 100644 index 0000000..674ad5a --- /dev/null +++ b/packages/react-native/src/components/DTSwitch.tsx @@ -0,0 +1,152 @@ +/** + * DT Switch Component + * + * Custom-built angular toggle switch following the Dangerous Things design language. + * RNP Switch delegates to native with no shape control, + * so we build a rectangular toggle from scratch. + */ + +import {useRef, useEffect} from 'react'; +import { + StyleSheet, + ViewStyle, + StyleProp, + Pressable, + Animated, +} from 'react-native'; +import {Text} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +const TRACK_WIDTH = 48; +const TRACK_HEIGHT = 26; +const THUMB_SIZE = 20; +const THUMB_MARGIN = 3; + +interface DTSwitchProps { + /** + * Whether the switch is on + */ + value: boolean; + /** + * Called when the value changes + */ + onValueChange: (value: boolean) => void; + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Whether the switch is disabled + */ + disabled?: boolean; + /** + * Label text displayed beside the switch + */ + label?: string; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Additional styles for the container + */ + style?: StyleProp; +} + +/** + * DT-styled angular Switch/Toggle + * + * @example + * + * + * @example + * + */ +export function DTSwitch({ + value, + onValueChange, + variant = 'normal', + disabled = false, + label, + color, + style, +}: DTSwitchProps) { + const accentColor = getVariantColor(variant, color); + const thumbAnim = useRef(new Animated.Value(value ? 1 : 0)).current; + + useEffect(() => { + Animated.timing(thumbAnim, { + toValue: value ? 1 : 0, + duration: 150, + useNativeDriver: false, + }).start(); + }, [value, thumbAnim]); + + const thumbTranslateX = thumbAnim.interpolate({ + inputRange: [0, 1], + outputRange: [THUMB_MARGIN, TRACK_WIDTH - THUMB_SIZE - THUMB_MARGIN], + }); + + const trackColor = thumbAnim.interpolate({ + inputRange: [0, 1], + outputRange: [DTColors.disabledBackground, accentColor], + }); + + + return ( + !disabled && onValueChange(!value)} + style={({pressed}) => [ + styles.container, + {opacity: disabled ? 0.5 : pressed ? 0.7 : 1}, + style, + ]}> + {label && ( + {label} + )} + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + paddingVertical: 8, + }, + label: { + fontSize: 14, + letterSpacing: 0.3, + }, + track: { + width: TRACK_WIDTH, + height: TRACK_HEIGHT, + borderWidth: 2, + justifyContent: 'center', + }, + thumb: { + width: THUMB_SIZE, + height: THUMB_SIZE, + }, +}); diff --git a/packages/react-native/src/components/DTTextInput.tsx b/packages/react-native/src/components/DTTextInput.tsx new file mode 100644 index 0000000..6bf951b --- /dev/null +++ b/packages/react-native/src/components/DTTextInput.tsx @@ -0,0 +1,172 @@ +/** + * DT TextInput Component + * + * A themed text input wrapping React Native Paper's TextInput + * with the Dangerous Things angular aesthetic. + * + * Web CSS reference (.mode-input): + * - 2px solid border in mode color + * - Focus: box-shadow 0 4px 0 1px var(--mode) + * - border-radius: 0 + */ + +import {useState, useRef, useEffect} from 'react'; +import { + StyleSheet, + View, + ViewStyle, + TextStyle, + StyleProp, + Animated, +} from 'react-native'; +import {TextInput, TextInputProps} from 'react-native-paper'; +import {DTColors} from '../theme/colors'; +import {type DTVariant, getVariantColor} from '../utils/variantColors'; + +interface DTTextInputProps + extends Omit { + /** + * Visual variant + * @default 'normal' + */ + variant?: DTVariant; + /** + * Custom color (overrides variant) + */ + color?: string; + /** + * Border width + * @default 2 + */ + borderWidth?: number; + /** + * Whether the input is in error state + */ + error?: boolean; + /** + * Error message text displayed below input + */ + errorMessage?: string; + /** + * Additional styles for the outer wrapper + */ + containerStyle?: StyleProp; + /** + * Additional styles for the input text + */ + inputStyle?: StyleProp; +} + +/** + * DT-styled TextInput wrapping React Native Paper TextInput + * + * @example + * + * + * @example + * + */ +export function DTTextInput({ + variant = 'normal', + color, + borderWidth = 2, + error = false, + errorMessage, + containerStyle, + inputStyle, + onFocus, + onBlur, + style, + ...props +}: DTTextInputProps) { + const [focused, setFocused] = useState(false); + const focusAnim = useRef(new Animated.Value(0)).current; + + const accentColor = error + ? DTColors.modeWarning + : getVariantColor(variant, color); + + useEffect(() => { + const anim = Animated.timing(focusAnim, { + toValue: focused ? 1 : 0, + duration: 150, + useNativeDriver: false, + }); + anim.start(); + return () => anim.stop(); + }, [focused, focusAnim]); + + return ( + + + { + setFocused(true); + onFocus?.(e); + }} + onBlur={e => { + setFocused(false); + onBlur?.(e); + }} + style={[styles.input, inputStyle, style]} + textColor={DTColors.light} + placeholderTextColor={DTColors.disabled} + activeUnderlineColor="transparent" + underlineColor="transparent" + theme={{ + colors: { + primary: accentColor, + onSurfaceVariant: accentColor, + }, + roundness: 0, + }} + /> + + {/* Focus glow bar — simulates web box-shadow: 0 4px 0 1px */} + + {error && errorMessage && ( + + + {errorMessage} + + + )} + + ); +} + +const styles = StyleSheet.create({ + inputWrapper: { + backgroundColor: DTColors.dark, + }, + input: { + backgroundColor: 'transparent', + }, + focusBar: { + height: 4, + marginTop: 1, + }, + errorContainer: { + marginTop: 4, + paddingHorizontal: 4, + }, + errorText: { + fontSize: 12, + letterSpacing: 0.5, + }, +}); diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts new file mode 100644 index 0000000..d424b7c --- /dev/null +++ b/packages/react-native/src/index.ts @@ -0,0 +1,77 @@ +/** + * @anthropic-dangerous-things/react-native-theme + * + * Dangerous Things design system for React Native with React Native Paper. + * Cyberpunk aesthetic with high-contrast neon colors. + */ + +// Theme exports +export { DTColors, getColor } from './theme/colors'; +export type { DTColorKey } from './theme/colors'; + +export { + DTTypography, + DTFonts, + DTFontWeights, +} from './theme/typography'; +export type { DTTypographyKey } from './theme/typography'; + +export { + DTDarkTheme, + DTExtendedDarkTheme, + usePaperTheme, +} from './theme/paperTheme'; +export type { DTExtendedTheme } from './theme/paperTheme'; + +export { DTThemeProvider, useDTTheme } from './theme/DTThemeProvider'; + +// Shared utilities +export type { DTVariant } from './utils/variantColors'; +export { variantColorMap, getVariantColor } from './utils/variantColors'; +export { buildBeveledRectPath } from './utils/bevelPaths'; +export { useComponentLayout } from './utils/useComponentLayout'; + +// Component exports — existing +export { DTCard, DTCardClipPath } from './components/DTCard'; +export { DTButton } from './components/DTButton'; +export { DTChip } from './components/DTChip'; +export { DTLabel, DTLabelClipPath } from './components/DTLabel'; + +// Component exports — form primitives +export { DTTextInput } from './components/DTTextInput'; +export { DTCheckbox } from './components/DTCheckbox'; +export { DTSwitch } from './components/DTSwitch'; +export { DTRadioGroup, DTRadioOption } from './components/DTRadioGroup'; +export { DTQuantityStepper } from './components/DTQuantityStepper'; + +// Component exports — layout & containers +export { DTProgressBar } from './components/DTProgressBar'; +export { DTMediaFrame } from './components/DTMediaFrame'; +export { DTAccordion } from './components/DTAccordion'; +export type { DTAccordionSection } from './components/DTAccordion'; + +// Component exports — complex interactive +export { DTModal } from './components/DTModal'; +export { DTDrawer } from './components/DTDrawer'; +export { DTGallery } from './components/DTGallery'; +export type { DTGalleryItem } from './components/DTGallery'; +export { DTSearchInput } from './components/DTSearchInput'; + +// Component exports — navigation & decorative +export { DTMenu, DTMenuDropdown } from './components/DTMenu'; +export type { DTMenuItem } from './components/DTMenu'; +export { DTHexagon } from './components/DTHexagon'; + +// Re-export commonly used Paper components for convenience +export { + Text, + Surface, + IconButton, + ActivityIndicator, + Divider, + List, + Portal, + Modal, + Snackbar, + TextInput, +} from 'react-native-paper'; diff --git a/packages/react-native/src/theme/DTThemeProvider.tsx b/packages/react-native/src/theme/DTThemeProvider.tsx new file mode 100644 index 0000000..cc4c0ee --- /dev/null +++ b/packages/react-native/src/theme/DTThemeProvider.tsx @@ -0,0 +1,80 @@ +/** + * DT Theme Provider + * + * Wraps your app with the Dangerous Things theme. + * Combines React Native Paper's PaperProvider with + * custom theme context for extended DT colors. + */ + +import { createContext, useContext, ReactNode } from 'react'; +import { PaperProvider } from 'react-native-paper'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { StatusBar } from 'react-native'; +import { DTExtendedDarkTheme, DTExtendedTheme } from './paperTheme'; + +/** + * Context for accessing extended theme properties + */ +const DTThemeContext = createContext(DTExtendedDarkTheme); + +/** + * Hook to access the full DT theme including custom properties + * + * @example + * const theme = useDTTheme(); + * const successColor = theme.custom.success; + */ +export function useDTTheme(): DTExtendedTheme { + return useContext(DTThemeContext); +} + +interface DTThemeProviderProps { + children: ReactNode; + /** + * Override the default theme (useful for testing or custom variants) + */ + theme?: DTExtendedTheme; + /** + * Include SafeAreaProvider wrapper (default: true) + * Set to false if you already have SafeAreaProvider higher in tree + */ + includeSafeArea?: boolean; +} + +/** + * Theme provider component + * + * @example + * // In your App.tsx + * import { DTThemeProvider } from '@anthropic-dangerous-things/react-native-theme'; + * + * export default function App() { + * return ( + * + * + * + * + * + * ); + * } + */ +export function DTThemeProvider({ + children, + theme = DTExtendedDarkTheme, + includeSafeArea = true, +}: DTThemeProviderProps) { + const content = ( + + + + {children} + + + ); + + if (includeSafeArea) { + return {content}; + } + + return content; +} diff --git a/packages/react-native/src/theme/colors.ts b/packages/react-native/src/theme/colors.ts new file mode 100644 index 0000000..bc26efa --- /dev/null +++ b/packages/react-native/src/theme/colors.ts @@ -0,0 +1,69 @@ +/** + * Dangerous Things Color Palette + * + * Based on the dt-shopify-storefront design system. + * Cyberpunk aesthetic with high-contrast neon colors on dark backgrounds. + */ + +export const DTColors = { + // Base colors + dark: '#000000', + light: '#FFFFFF', + + // Mode colors (primary semantic palette) + modeNormal: '#00FFFF', // Cyan - primary actions, links, borders + modeNormalSelected: 'rgba(0, 255, 255, 0.7)', + modeEmphasis: '#FFFF00', // Yellow - highlights, emphasis, headers + modeEmphasisSelected: 'rgba(255, 255, 0, 0.7)', + modeWarning: '#FF0000', // Red - errors, warnings, destructive actions + modeSuccess: '#00FF00', // Green - success states, confirmations + modeOther: '#FF00FF', // Magenta - miscellaneous, secondary info + + // Semantic aliases + primary: '#00FFFF', + primaryVariant: 'rgba(0, 255, 255, 0.7)', + secondary: '#FFFF00', + secondaryVariant: 'rgba(255, 255, 0, 0.7)', + error: '#FF0000', + success: '#00FF00', + + // Surface colors + background: '#000000', + surface: '#000000', + surfaceVariant: '#070707', + + // Text colors + onBackground: '#FFFFFF', + onSurface: '#FFFFFF', + onPrimary: '#000000', + onSecondary: '#000000', + onError: '#000000', + + // Border colors + border: '#00FFFF', + borderEmphasis: '#FFFF00', + + // Transparency variants (for overlays, disabled states) + overlay: 'rgba(0, 0, 0, 0.7)', + disabled: 'rgba(255, 255, 255, 0.3)', + disabledBackground: 'rgba(255, 255, 255, 0.1)', +} as const; + +export type DTColorKey = keyof typeof DTColors; + +/** + * Get a color with optional opacity override + */ +export function getColor(color: DTColorKey, opacity?: number): string { + const baseColor = DTColors[color]; + if (opacity === undefined || baseColor.startsWith('rgba')) { + return baseColor; + } + + // Convert hex to rgba + const hex = baseColor.replace('#', ''); + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + return `rgba(${r}, ${g}, ${b}, ${opacity})`; +} diff --git a/packages/react-native/src/theme/paperTheme.ts b/packages/react-native/src/theme/paperTheme.ts new file mode 100644 index 0000000..2162341 --- /dev/null +++ b/packages/react-native/src/theme/paperTheme.ts @@ -0,0 +1,149 @@ +/** + * React Native Paper MD3 Theme Configuration + * + * This creates a Material Design 3 compliant theme that follows + * the Dangerous Things cyberpunk aesthetic while respecting + * React Native Paper's design principles. + */ + +import { MD3DarkTheme, MD3Theme, configureFonts } from 'react-native-paper'; +import { DTColors } from './colors'; +import { DTTypography, DTFonts } from './typography'; + +/** + * MD3 Font configuration for React Native Paper + * Maps our typography to RNP's expected font config + */ +const fontConfig = { + fontFamily: DTFonts.regular, + displayLarge: DTTypography.displayLarge, + displayMedium: DTTypography.displayMedium, + displaySmall: DTTypography.displaySmall, + headlineLarge: DTTypography.headlineLarge, + headlineMedium: DTTypography.headlineMedium, + headlineSmall: DTTypography.headlineSmall, + titleLarge: DTTypography.titleLarge, + titleMedium: DTTypography.titleMedium, + titleSmall: DTTypography.titleSmall, + bodyLarge: DTTypography.bodyLarge, + bodyMedium: DTTypography.bodyMedium, + bodySmall: DTTypography.bodySmall, + labelLarge: DTTypography.labelLarge, + labelMedium: DTTypography.labelMedium, + labelSmall: DTTypography.labelSmall, +}; + +/** + * Dangerous Things Dark Theme + * + * Based on MD3DarkTheme with cyberpunk color overrides. + * Primary = Cyan, Secondary = Yellow (following DT brand) + */ +export const DTDarkTheme: MD3Theme = { + ...MD3DarkTheme, + dark: true, + roundness: 4, // Slightly rounded, but we use custom clip-path for bevels + + fonts: configureFonts({ config: fontConfig }), + + colors: { + ...MD3DarkTheme.colors, + + // Primary (Cyan) + primary: DTColors.modeNormal, + onPrimary: DTColors.dark, + primaryContainer: DTColors.modeNormalSelected, + onPrimaryContainer: DTColors.dark, + + // Secondary (Yellow) + secondary: DTColors.modeEmphasis, + onSecondary: DTColors.dark, + secondaryContainer: DTColors.modeEmphasisSelected, + onSecondaryContainer: DTColors.dark, + + // Tertiary (Magenta) + tertiary: DTColors.modeOther, + onTertiary: DTColors.dark, + tertiaryContainer: 'rgba(255, 0, 255, 0.3)', + onTertiaryContainer: DTColors.light, + + // Error (Red) + error: DTColors.modeWarning, + onError: DTColors.dark, + errorContainer: 'rgba(255, 0, 0, 0.3)', + onErrorContainer: DTColors.light, + + // Background & Surface + background: DTColors.background, + onBackground: DTColors.onBackground, + surface: DTColors.surface, + onSurface: DTColors.onSurface, + surfaceVariant: DTColors.surfaceVariant, + onSurfaceVariant: DTColors.light, + + // Surface elevation (MD3 uses tonal elevation) + elevation: { + level0: 'transparent', + level1: DTColors.surfaceVariant, + level2: '#0a0a0a', + level3: '#0f0f0f', + level4: '#121212', + level5: '#151515', + }, + + // Outline (borders) + outline: DTColors.modeNormal, + outlineVariant: 'rgba(0, 255, 255, 0.5)', + + // Inverse (for snackbars, etc.) + inverseSurface: DTColors.light, + inverseOnSurface: DTColors.dark, + inversePrimary: '#008B8B', // Darker cyan for light backgrounds + + // Misc + shadow: DTColors.dark, + scrim: DTColors.overlay, + surfaceDisabled: DTColors.disabledBackground, + onSurfaceDisabled: DTColors.disabled, + backdrop: DTColors.overlay, + }, +}; + +/** + * Extended theme with DT-specific custom properties + * Use this when you need access to non-MD3 colors + */ +export interface DTExtendedTheme extends MD3Theme { + custom: { + success: string; + warning: string; + modeNormal: string; + modeEmphasis: string; + modeWarning: string; + modeSuccess: string; + modeOther: string; + border: string; + borderEmphasis: string; + }; +} + +export const DTExtendedDarkTheme: DTExtendedTheme = { + ...DTDarkTheme, + custom: { + success: DTColors.modeSuccess, + warning: DTColors.modeWarning, + modeNormal: DTColors.modeNormal, + modeEmphasis: DTColors.modeEmphasis, + modeWarning: DTColors.modeWarning, + modeSuccess: DTColors.modeSuccess, + modeOther: DTColors.modeOther, + border: DTColors.border, + borderEmphasis: DTColors.borderEmphasis, + }, +}; + +/** + * Hook to access extended theme properties + * Usage: const { custom } = useDTTheme(); + */ +export { useTheme as usePaperTheme } from 'react-native-paper'; diff --git a/packages/react-native/src/theme/typography.ts b/packages/react-native/src/theme/typography.ts new file mode 100644 index 0000000..37fefcd --- /dev/null +++ b/packages/react-native/src/theme/typography.ts @@ -0,0 +1,190 @@ +/** + * Dangerous Things Typography + * + * Primary font: Tektur (variable font) + * Fallback: System default sans-serif + * + * Note: To use Tektur, you must: + * 1. Download Tektur from Google Fonts + * 2. Add to your project's assets/fonts/ + * 3. Link fonts in react-native.config.js or use expo-font + */ + +import { Platform, TextStyle } from 'react-native'; + +/** + * Font family configuration + * Tektur is a variable font with weights 100-900 + */ +export const DTFonts = { + regular: Platform.select({ + ios: 'Tektur', + android: 'Tektur-Regular', + default: 'Tektur', + }), + medium: Platform.select({ + ios: 'Tektur', + android: 'Tektur-Medium', + default: 'Tektur', + }), + bold: Platform.select({ + ios: 'Tektur', + android: 'Tektur-Bold', + default: 'Tektur', + }), + // Fallback when Tektur is not available + fallback: Platform.select({ + ios: 'System', + android: 'sans-serif', + default: 'sans-serif', + }), +} as const; + +/** + * Font weights for variable font + */ +export const DTFontWeights = { + thin: '100' as const, + light: '300' as const, + regular: '400' as const, + medium: '500' as const, + semibold: '600' as const, + bold: '700' as const, + extrabold: '800' as const, + black: '900' as const, +}; + +/** + * Base text styles + */ +const baseTextStyle: TextStyle = { + fontFamily: DTFonts.regular, + color: '#FFFFFF', + letterSpacing: 0.5, +}; + +/** + * Typography scale following Material Design 3 naming + */ +export const DTTypography = { + // Display styles (large headlines) + displayLarge: { + ...baseTextStyle, + fontSize: 57, + fontWeight: DTFontWeights.bold, + lineHeight: 64, + letterSpacing: -0.25, + } as TextStyle, + + displayMedium: { + ...baseTextStyle, + fontSize: 45, + fontWeight: DTFontWeights.bold, + lineHeight: 52, + } as TextStyle, + + displaySmall: { + ...baseTextStyle, + fontSize: 36, + fontWeight: DTFontWeights.bold, + lineHeight: 44, + } as TextStyle, + + // Headline styles + headlineLarge: { + ...baseTextStyle, + fontSize: 32, + fontWeight: DTFontWeights.semibold, + lineHeight: 40, + } as TextStyle, + + headlineMedium: { + ...baseTextStyle, + fontSize: 28, + fontWeight: DTFontWeights.semibold, + lineHeight: 36, + } as TextStyle, + + headlineSmall: { + ...baseTextStyle, + fontSize: 24, + fontWeight: DTFontWeights.semibold, + lineHeight: 32, + } as TextStyle, + + // Title styles + titleLarge: { + ...baseTextStyle, + fontSize: 22, + fontWeight: DTFontWeights.medium, + lineHeight: 28, + } as TextStyle, + + titleMedium: { + ...baseTextStyle, + fontSize: 16, + fontWeight: DTFontWeights.medium, + lineHeight: 24, + letterSpacing: 0.15, + } as TextStyle, + + titleSmall: { + ...baseTextStyle, + fontSize: 14, + fontWeight: DTFontWeights.medium, + lineHeight: 20, + letterSpacing: 0.1, + } as TextStyle, + + // Body styles + bodyLarge: { + ...baseTextStyle, + fontSize: 16, + fontWeight: DTFontWeights.regular, + lineHeight: 24, + letterSpacing: 0.5, + } as TextStyle, + + bodyMedium: { + ...baseTextStyle, + fontSize: 14, + fontWeight: DTFontWeights.regular, + lineHeight: 20, + letterSpacing: 0.25, + } as TextStyle, + + bodySmall: { + ...baseTextStyle, + fontSize: 12, + fontWeight: DTFontWeights.regular, + lineHeight: 16, + letterSpacing: 0.4, + } as TextStyle, + + // Label styles + labelLarge: { + ...baseTextStyle, + fontSize: 14, + fontWeight: DTFontWeights.medium, + lineHeight: 20, + letterSpacing: 0.1, + } as TextStyle, + + labelMedium: { + ...baseTextStyle, + fontSize: 12, + fontWeight: DTFontWeights.medium, + lineHeight: 16, + letterSpacing: 0.5, + } as TextStyle, + + labelSmall: { + ...baseTextStyle, + fontSize: 11, + fontWeight: DTFontWeights.medium, + lineHeight: 16, + letterSpacing: 0.5, + } as TextStyle, +} as const; + +export type DTTypographyKey = keyof typeof DTTypography; diff --git a/packages/react-native/src/utils/bevelPaths.ts b/packages/react-native/src/utils/bevelPaths.ts new file mode 100644 index 0000000..0f75dc1 --- /dev/null +++ b/packages/react-native/src/utils/bevelPaths.ts @@ -0,0 +1,152 @@ +/** + * Shared SVG bevel path builders + * + * Generic and preset functions for generating SVG path data + * for beveled rectangle shapes used across DT components. + */ + +type BevelCorner = 'topLeft' | 'topRight' | 'bottomRight' | 'bottomLeft'; + +interface BevelConfig { + corners: Partial>; + strokeWidth?: number; + /** Additional inset applied to all coordinates (used for frame overlays) */ + offset?: number; +} + +/** + * Build an SVG path for a rectangle with any combination of beveled corners. + * + * Each corner can have an independent bevel size. Corners without a bevel + * value (or with 0) are rendered as sharp 90-degree angles. + */ +export function buildBeveledRectPath( + width: number, + height: number, + config: BevelConfig, +): string { + const sw = config.strokeWidth ?? 0; + const off = config.offset ?? 0; + const inset = sw / 2 + off; + const w = width - sw - off * 2; + const h = height - sw - off * 2; + + if (w <= 0 || h <= 0) return ''; + + const maxBevel = Math.min(w / 3, h / 3); + const tl = Math.min(config.corners.topLeft ?? 0, maxBevel); + const tr = Math.min(config.corners.topRight ?? 0, maxBevel); + const br = Math.min(config.corners.bottomRight ?? 0, maxBevel); + const bl = Math.min(config.corners.bottomLeft ?? 0, maxBevel); + + // Build path clockwise from top-left + return [ + // Top-left corner + `M ${inset + tl} ${inset}`, + // Top edge → top-right corner + `L ${w + inset - tr} ${inset}`, + tr > 0 ? `L ${w + inset} ${inset + tr}` : `L ${w + inset} ${inset}`, + // Right edge → bottom-right corner + br > 0 + ? `L ${w + inset} ${h + inset - br} L ${w + inset - br} ${h + inset}` + : `L ${w + inset} ${h + inset}`, + // Bottom edge → bottom-left corner + bl > 0 + ? `L ${inset + bl} ${h + inset} L ${inset} ${h + inset - bl}` + : `L ${inset} ${h + inset}`, + // Left edge → back to top-left + tl > 0 ? `L ${inset} ${inset + tl}` : `L ${inset} ${inset}`, + 'Z', + ].join(' '); +} + +/** + * DTButton shape: bottom-right bevel only + */ +export function buildButtonBevelPath( + width: number, + height: number, + bevelSize: number, + strokeWidth: number, +): string { + return buildBeveledRectPath(width, height, { + corners: {bottomRight: bevelSize}, + strokeWidth, + }); +} + +/** + * DTCard shape: bottom-right (large) + bottom-left (small) bevels + */ +export function buildCardBevelPath( + width: number, + height: number, + bevelBR: number, + bevelBL: number, + inset: number, +): string { + const w = width - inset * 2; + const h = height - inset * 2; + + if (w <= 0 || h <= 0) return ''; + + const br = Math.min(bevelBR, w / 3, h / 3); + const bl = Math.min(bevelBL, w / 3, h / 3); + + return ` + M ${inset} ${inset} + L ${w + inset} ${inset} + L ${w + inset} ${h - br + inset} + L ${w - br + inset} ${h + inset} + L ${bl + inset} ${h + inset} + L ${inset} ${h - bl + inset} + Z + `; +} + +/** + * DTLabel shape: top-right bevel only + */ +export function buildLabelBevelPath( + width: number, + height: number, + bevelSize: number, +): string { + return buildBeveledRectPath(width, height, { + corners: {topRight: bevelSize}, + }); +} + +/** + * DTMediaFrame shape: top-left + bottom-right (diagonal symmetry) + */ +export function buildMediaFrameBevelPath( + width: number, + height: number, + bevelSize: number, + offset = 0, +): string { + return buildBeveledRectPath(width, height, { + corners: {topLeft: bevelSize, bottomRight: bevelSize}, + offset, + }); +} + +/** + * DTDrawer shape (right-side): top-left + bottom-left bevels + */ +export function buildDrawerBevelPath( + width: number, + height: number, + bevelSize: number, + position: 'left' | 'right' = 'right', +): string { + if (position === 'right') { + return buildBeveledRectPath(width, height, { + corners: {topLeft: bevelSize, bottomLeft: bevelSize}, + }); + } + return buildBeveledRectPath(width, height, { + corners: {topRight: bevelSize, bottomRight: bevelSize}, + }); +} diff --git a/packages/react-native/src/utils/useComponentLayout.ts b/packages/react-native/src/utils/useComponentLayout.ts new file mode 100644 index 0000000..3abeff7 --- /dev/null +++ b/packages/react-native/src/utils/useComponentLayout.ts @@ -0,0 +1,33 @@ +/** + * Shared layout measurement hook + * + * Extracts the common pattern of tracking component dimensions + * via onLayout for SVG rendering. + */ + +import {useState, useCallback} from 'react'; +import {LayoutChangeEvent} from 'react-native'; + +interface ComponentDimensions { + width: number; + height: number; +} + +export function useComponentLayout() { + const [dimensions, setDimensions] = useState({ + width: 0, + height: 0, + }); + + const onLayout = useCallback((event: LayoutChangeEvent) => { + const {width, height} = event.nativeEvent.layout; + setDimensions(prev => { + if (prev.width === width && prev.height === height) return prev; + return {width, height}; + }); + }, []); + + const hasDimensions = dimensions.width > 0 && dimensions.height > 0; + + return {dimensions, onLayout, hasDimensions}; +} diff --git a/packages/react-native/src/utils/variantColors.ts b/packages/react-native/src/utils/variantColors.ts new file mode 100644 index 0000000..324fb97 --- /dev/null +++ b/packages/react-native/src/utils/variantColors.ts @@ -0,0 +1,27 @@ +/** + * Centralized variant type and color mapping + * + * Used by all DT components for consistent mode/variant color resolution. + */ + +import {DTColors} from '../theme/colors'; + +export type DTVariant = 'normal' | 'emphasis' | 'warning' | 'success' | 'other'; + +export const variantColorMap: Record = { + normal: DTColors.modeNormal, + emphasis: DTColors.modeEmphasis, + warning: DTColors.modeWarning, + success: DTColors.modeSuccess, + other: DTColors.modeOther, +}; + +/** + * Resolve variant to color, with optional custom color override. + */ +export function getVariantColor( + variant: DTVariant, + customColor?: string, +): string { + return customColor ?? variantColorMap[variant]; +} diff --git a/packages/react-native/tsconfig.json b/packages/react-native/tsconfig.json new file mode 100644 index 0000000..498e325 --- /dev/null +++ b/packages/react-native/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "jsx": "react-jsx", + "lib": ["ES2022"], + "allowSyntheticDefaultImports": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/tokens/package.json b/packages/tokens/package.json new file mode 100644 index 0000000..32599b9 --- /dev/null +++ b/packages/tokens/package.json @@ -0,0 +1,36 @@ +{ + "name": "@dangerousthings/tokens", + "version": "0.1.0", + "description": "Canonical design tokens for the Dangerous Things design system", + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./brands/*": { + "types": "./dist/brands/*.d.ts", + "import": "./dist/brands/*.js" + }, + "./css/*": "./dist/css/*" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc && node dist/scripts/generate-css.js", + "clean": "rm -rf dist", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/node": "^25.3.3", + "typescript": "^5.7.0" + } +} diff --git a/packages/tokens/src/brands/classic.ts b/packages/tokens/src/brands/classic.ts new file mode 100644 index 0000000..0c78530 --- /dev/null +++ b/packages/tokens/src/brands/classic.ts @@ -0,0 +1,66 @@ +import type { BrandTokens } from "../types.js"; + +export const classic: BrandTokens = { + id: "classic", + name: "Classic Cyberpunk", + description: "Original dark navy aesthetic with magenta accents", + + dark: { + bg: "#0a0e1a", + surface: "#121827", + surfaceHover: "#1a2332", + border: "#1e293b", + + textPrimary: "#e2e8f0", + textSecondary: "#94a3b8", + textMuted: "#64748b", + + primary: "#00ffff", + primaryDim: "#0891b2", + secondary: "#ff00ff", + accent: "#00ff88", + other: "#ff00ff", + + success: "#00ff88", + warning: "#ffaa00", + error: "#ff0055", + info: "#00ffff", + }, + + light: { + bg: "#ffffff", + surface: "#f8fafc", + surfaceHover: "#f1f5f9", + border: "#e2e8f0", + + textPrimary: "#0f172a", + textSecondary: "#475569", + textMuted: "#94a3b8", + + primary: "#0891b2", + primaryDim: "#0e7490", + secondary: "#c026d3", + accent: "#00ff88", + other: "#c026d3", + + success: "#16a34a", + warning: "#d97706", + error: "#dc2626", + info: "#0891b2", + }, + + typography: { + heading: "var(--font-sans)", + body: "var(--font-sans)", + mono: "'SF Mono', Monaco, 'Cascadia Code', monospace", + }, + + shape: { + bevelSm: "0px", + bevelMd: "0px", + bevelLg: "0px", + radiusSm: "0.25rem", + radius: "0.5rem", + radiusLg: "0.75rem", + }, +}; diff --git a/packages/tokens/src/brands/dt.ts b/packages/tokens/src/brands/dt.ts new file mode 100644 index 0000000..2ef9d07 --- /dev/null +++ b/packages/tokens/src/brands/dt.ts @@ -0,0 +1,66 @@ +import type { BrandTokens } from "../types.js"; + +export const dt: BrandTokens = { + id: "dt", + name: "Dangerous Things", + description: "Official DT brand — Tektur font, neon cyberpunk, beveled corners", + + dark: { + bg: "#000000", + surface: "#0a0a0a", + surfaceHover: "#141414", + border: "#00ffff", + + textPrimary: "#ffffff", + textSecondary: "#e2e8f0", + textMuted: "#94a3b8", + + primary: "#00ffff", + primaryDim: "#0891b2", + secondary: "#ffff00", + accent: "#00ff00", + other: "#ff00ff", + + success: "#00ff00", + warning: "#ffff00", + error: "#ff0000", + info: "#00ffff", + }, + + light: { + bg: "#ffffff", + surface: "#f8fafc", + surfaceHover: "#f1f5f9", + border: "#000000", + + textPrimary: "#000000", + textSecondary: "#475569", + textMuted: "#94a3b8", + + primary: "#0891b2", + primaryDim: "#0e7490", + secondary: "#a16207", + accent: "#15803d", + other: "#ff00ff", + + success: "#15803d", + warning: "#a16207", + error: "#dc2626", + info: "#0891b2", + }, + + typography: { + heading: "'Tektur', var(--font-sans)", + body: "'Tektur', var(--font-sans)", + mono: "'SF Mono', Monaco, 'Cascadia Code', monospace", + }, + + shape: { + bevelSm: "1rem", + bevelMd: "2rem", + bevelLg: "2.5rem", + radiusSm: "0", + radius: "0", + radiusLg: "0", + }, +}; diff --git a/packages/tokens/src/brands/supra.ts b/packages/tokens/src/brands/supra.ts new file mode 100644 index 0000000..40d5cc4 --- /dev/null +++ b/packages/tokens/src/brands/supra.ts @@ -0,0 +1,66 @@ +import type { BrandTokens } from "../types.js"; + +export const supra: BrandTokens = { + id: "supra", + name: "VivoKey Supra", + description: "Material Design 3 with VivoKey blue, rounded corners, elevation shadows", + + dark: { + bg: "#000000", + surface: "#28292E", + surfaceHover: "#303136", + border: "#42667E", + + textPrimary: "#ffffff", + textSecondary: "#e2e8f0", + textMuted: "#94a3b8", + + primary: "#42667E", + primaryDim: "#385872", + secondary: "#8BAEC7", + accent: "#8BAEC7", + other: "#F67448", + + success: "#8BB174", + warning: "#F67448", + error: "#ED4337", + info: "#42667E", + }, + + light: { + bg: "#ffffff", + surface: "#AFB8C2", + surfaceHover: "#C5CCD4", + border: "#000000", + + textPrimary: "#000000", + textSecondary: "#374151", + textMuted: "#6b7280", + + primary: "#000000", + primaryDim: "#1f2937", + secondary: "#F67448", + accent: "#FDFE1B", + other: "#F67448", + + success: "#8BB174", + warning: "#F67448", + error: "#ED4337", + info: "#42667E", + }, + + typography: { + heading: "var(--font-sans)", + body: "var(--font-sans)", + mono: "'SF Mono', Monaco, 'Cascadia Code', monospace", + }, + + shape: { + bevelSm: "0px", + bevelMd: "0px", + bevelLg: "0px", + radiusSm: "0.5rem", + radius: "0.625rem", + radiusLg: "0.75rem", + }, +}; diff --git a/packages/tokens/src/index.ts b/packages/tokens/src/index.ts new file mode 100644 index 0000000..a27a127 --- /dev/null +++ b/packages/tokens/src/index.ts @@ -0,0 +1,46 @@ +export type { + ThemeBrand, + ThemeMode, + ThemeDefinition, + ColorTokens, + TypographyTokens, + ShapeTokens, + BrandTokens, +} from "./types.js"; + +export { dt } from "./brands/dt.js"; +export { classic } from "./brands/classic.js"; +export { supra } from "./brands/supra.js"; + +import { dt } from "./brands/dt.js"; +import { classic } from "./brands/classic.js"; +import { supra } from "./brands/supra.js"; +import type { BrandTokens, ThemeDefinition } from "./types.js"; + +/** All brand tokens keyed by id */ +export const brands: Record = { dt, classic, supra }; + +/** Theme registry for UI selectors */ +export const themes: ThemeDefinition[] = [ + { + id: "dt", + name: dt.name, + description: dt.description, + supportsModes: ["dark", "light", "auto"], + defaultMode: "dark", + }, + { + id: "classic", + name: classic.name, + description: classic.description, + supportsModes: ["dark", "light", "auto"], + defaultMode: "dark", + }, + { + id: "supra", + name: supra.name, + description: supra.description, + supportsModes: ["dark", "light", "auto"], + defaultMode: "dark", + }, +]; diff --git a/packages/tokens/src/scripts/generate-css.ts b/packages/tokens/src/scripts/generate-css.ts new file mode 100644 index 0000000..7180787 --- /dev/null +++ b/packages/tokens/src/scripts/generate-css.ts @@ -0,0 +1,129 @@ +/** + * Generates CSS custom property files from canonical TypeScript token definitions. + * Run via: node dist/scripts/generate-css.js + * Output: dist/css/{brand}.css + */ +import { writeFileSync, mkdirSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import type { BrandTokens, ColorTokens } from "../types.js"; +import { dt } from "../brands/dt.js"; +import { classic } from "../brands/classic.js"; +import { supra } from "../brands/supra.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const outDir = join(__dirname, "..", "css"); + +/** Convert hex #rrggbb to "r, g, b" triplet for rgba() usage */ +function hexToRgb(hex: string): string { + const h = hex.replace("#", ""); + const r = parseInt(h.substring(0, 2), 16); + const g = parseInt(h.substring(2, 4), 16); + const b = parseInt(h.substring(4, 6), 16); + return `${r}, ${g}, ${b}`; +} + +/** Generate CSS custom properties from color tokens */ +function colorVars(colors: ColorTokens): string { + return ` /* Backgrounds */ + --color-bg: ${colors.bg}; + --color-bg-rgb: ${hexToRgb(colors.bg)}; + --color-surface: ${colors.surface}; + --color-surface-rgb: ${hexToRgb(colors.surface)}; + --color-surface-hover: ${colors.surfaceHover}; + --color-border: ${colors.border}; + + /* Text */ + --color-text-primary: ${colors.textPrimary}; + --color-text-secondary: ${colors.textSecondary}; + --color-text-muted: ${colors.textMuted}; + + /* Brand Palette */ + --color-primary: ${colors.primary}; + --color-primary-rgb: ${hexToRgb(colors.primary)}; + --color-primary-dim: ${colors.primaryDim}; + --color-secondary: ${colors.secondary}; + --color-secondary-rgb: ${hexToRgb(colors.secondary)}; + --color-accent: ${colors.accent}; + --color-accent-rgb: ${hexToRgb(colors.accent)}; + --color-other: ${colors.other}; + --color-other-rgb: ${hexToRgb(colors.other)}; + + /* Status */ + --color-success: ${colors.success}; + --color-success-rgb: ${hexToRgb(colors.success)}; + --color-warning: ${colors.warning}; + --color-warning-rgb: ${hexToRgb(colors.warning)}; + --color-error: ${colors.error}; + --color-error-rgb: ${hexToRgb(colors.error)}; + --color-info: ${colors.info}; + --color-info-rgb: ${hexToRgb(colors.info)};`; +} + +/** Generate a full brand CSS file */ +function generateBrandCSS(brand: BrandTokens): string { + const id = brand.id; + + // DT is also the default (when no data-brand is set) + const darkSelector = + id === "dt" + ? `:root[data-brand="${id}"],\n:root:not([data-brand])` + : `:root[data-brand="${id}"]`; + + let css = `/* @dangerousthings/tokens — Generated CSS for "${brand.name}" */\n/* DO NOT EDIT — regenerate with: npm run build -w packages/tokens */\n\n`; + + // Dark mode (default) + css += `/* ${brand.name} — Dark Mode (Default) */\n`; + css += `${darkSelector} {\n`; + css += colorVars(brand.dark) + "\n\n"; + css += ` /* Typography */\n`; + css += ` --font-heading: ${brand.typography.heading};\n`; + css += ` --font-body: ${brand.typography.body};\n\n`; + css += ` /* Shape */\n`; + css += ` --bevel-sm: ${brand.shape.bevelSm};\n`; + css += ` --bevel-md: ${brand.shape.bevelMd};\n`; + css += ` --bevel-lg: ${brand.shape.bevelLg};\n`; + css += ` --radius-sm: ${brand.shape.radiusSm};\n`; + css += ` --radius: ${brand.shape.radius};\n`; + css += ` --radius-lg: ${brand.shape.radiusLg};\n\n`; + css += ` color-scheme: dark;\n`; + css += `}\n\n`; + + // Light mode (explicit) + css += `/* ${brand.name} — Light Mode */\n`; + css += `:root[data-brand="${id}"][data-theme="light"] {\n`; + css += colorVars(brand.light) + "\n\n"; + css += ` color-scheme: light;\n`; + css += `}\n\n`; + + // Auto mode (prefers-color-scheme) + css += `/* ${brand.name} — Auto Mode (follows system preference) */\n`; + css += `@media (prefers-color-scheme: light) {\n`; + css += ` :root[data-brand="${id}"][data-theme="auto"] {\n`; + css += colorVars(brand.light).replace(/^/gm, " ") + "\n\n"; + css += ` color-scheme: light;\n`; + css += ` }\n`; + css += `}\n`; + + // DT gets explicit dark override + if (id === "dt") { + css += `\n/* Force dark explicitly */\n`; + css += `:root[data-brand="${id}"][data-theme="dark"] {\n`; + css += ` color-scheme: dark;\n`; + css += `}\n`; + } + + return css; +} + +// --- Main --- +mkdirSync(outDir, { recursive: true }); + +for (const brand of [dt, classic, supra]) { + const css = generateBrandCSS(brand); + const path = join(outDir, `${brand.id}.css`); + writeFileSync(path, css, "utf-8"); + console.log(` tokens: generated ${path}`); +} + +console.log(" tokens: CSS generation complete"); diff --git a/packages/tokens/src/types.ts b/packages/tokens/src/types.ts new file mode 100644 index 0000000..9818bf7 --- /dev/null +++ b/packages/tokens/src/types.ts @@ -0,0 +1,65 @@ +/** Brand identifiers */ +export type ThemeBrand = "dt" | "classic" | "supra"; + +/** Color mode */ +export type ThemeMode = "dark" | "light" | "auto"; + +/** Theme metadata for UI selectors */ +export interface ThemeDefinition { + id: ThemeBrand; + name: string; + description: string; + supportsModes: ThemeMode[]; + defaultMode: ThemeMode; +} + +/** Color tokens for a single mode */ +export interface ColorTokens { + bg: string; + surface: string; + surfaceHover: string; + border: string; + + textPrimary: string; + textSecondary: string; + textMuted: string; + + primary: string; + primaryDim: string; + secondary: string; + accent: string; + other: string; + + success: string; + warning: string; + error: string; + info: string; +} + +/** Typography tokens */ +export interface TypographyTokens { + heading: string; + body: string; + mono: string; +} + +/** Shape tokens */ +export interface ShapeTokens { + bevelSm: string; + bevelMd: string; + bevelLg: string; + radiusSm: string; + radius: string; + radiusLg: string; +} + +/** Complete brand definition */ +export interface BrandTokens { + id: ThemeBrand; + name: string; + description: string; + dark: ColorTokens; + light: ColorTokens; + typography: TypographyTokens; + shape: ShapeTokens; +} diff --git a/packages/tokens/tsconfig.json b/packages/tokens/tsconfig.json new file mode 100644 index 0000000..5a24989 --- /dev/null +++ b/packages/tokens/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/web/fonts/Tektur-VariableFont_wdth,wght.ttf b/packages/web/fonts/Tektur-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000000000000000000000000000000000000..05d4221f35b4505cf0494e1ff98d20cea94e49d6 GIT binary patch literal 162656 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 literal 0 HcmV?d00001 diff --git a/packages/web/package.json b/packages/web/package.json new file mode 100644 index 0000000..6e2fe8e --- /dev/null +++ b/packages/web/package.json @@ -0,0 +1,30 @@ +{ + "name": "@dangerousthings/web", + "version": "0.1.0", + "description": "Web CSS theme for the Dangerous Things design system — bevels, glows, form styles", + "license": "MIT", + "main": "dist/index.css", + "exports": { + ".": "./dist/index.css", + "./tokens/*": "./dist/tokens/*", + "./components/*": "./dist/components/*", + "./fonts/*": "./dist/fonts/*", + "./theme-registry": { + "types": "./dist/theme-registry.d.ts", + "import": "./dist/theme-registry.js", + "default": "./dist/theme-registry.js" + }, + "./dist/*": "./dist/*" + }, + "publishConfig": { + "access": "public" + }, + "files": ["dist"], + "scripts": { + "build": "node scripts/build.js", + "clean": "rm -rf dist" + }, + "dependencies": { + "@dangerousthings/tokens": "*" + } +} diff --git a/packages/web/scripts/build.js b/packages/web/scripts/build.js new file mode 100644 index 0000000..a9ddc6e --- /dev/null +++ b/packages/web/scripts/build.js @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +/** + * @dangerousthings/web build script + * + * 1. Copies src/ to dist/ (component CSS, base tokens, index.css) + * 2. Copies fonts/ to dist/fonts/ + * 3. Copies generated token CSS from @dangerousthings/tokens/dist/css/ into dist/tokens/ + * 4. Copies theme-registry from @dangerousthings/tokens + * + * Consumers (Vite, webpack) resolve @import chains at build time. + */ + +const fs = require("fs"); +const path = require("path"); + +const root = path.join(__dirname, ".."); +const src = path.join(root, "src"); +const dist = path.join(root, "dist"); +const fontsDir = path.join(root, "fonts"); + +// Resolve @dangerousthings/tokens dist directory +const tokensDir = path.join(root, "..", "tokens", "dist"); + +function copyDir(srcDir, destDir) { + fs.mkdirSync(destDir, { recursive: true }); + for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) { + const srcPath = path.join(srcDir, entry.name); + const destPath = path.join(destDir, entry.name); + if (entry.isDirectory()) { + copyDir(srcPath, destPath); + } else { + fs.copyFileSync(srcPath, destPath); + } + } +} + +// Clean dist +if (fs.existsSync(dist)) { + fs.rmSync(dist, { recursive: true }); +} + +// 1. Copy src → dist +copyDir(src, dist); + +// 2. Copy fonts → dist/fonts +if (fs.existsSync(fontsDir)) { + copyDir(fontsDir, path.join(dist, "fonts")); +} + +// 3. Copy generated token CSS from @dangerousthings/tokens → dist/tokens/ +const tokensCssDir = path.join(tokensDir, "css"); +if (fs.existsSync(tokensCssDir)) { + const destTokens = path.join(dist, "tokens"); + fs.mkdirSync(destTokens, { recursive: true }); + for (const file of fs.readdirSync(tokensCssDir)) { + if (file.endsWith(".css")) { + fs.copyFileSync( + path.join(tokensCssDir, file), + path.join(destTokens, file) + ); + } + } +} else { + console.error("ERROR: @dangerousthings/tokens CSS not found at", tokensCssDir); + console.error("Run: npm run build -w packages/tokens first"); + process.exit(1); +} + +// 4. Copy theme-registry from tokens +const registryFiles = ["theme-registry.js", "theme-registry.d.ts"]; +// The theme-registry is now in @dangerousthings/tokens as index.js — consumers +// import types from @dangerousthings/tokens directly. We generate a thin re-export +// for backward compatibility with dt-web-theme consumers. +const reExport = `// Re-export from @dangerousthings/tokens for backward compatibility +export { themes, brands } from "@dangerousthings/tokens"; +export { DEFAULT_THEME } from "@dangerousthings/tokens"; +`; +const reExportDts = `export { ThemeDefinition, ThemeBrand, ThemeMode } from "@dangerousthings/tokens"; +export declare const themes: import("@dangerousthings/tokens").ThemeDefinition[]; +export declare const brands: Record; +export declare const DEFAULT_THEME: string; +`; +fs.writeFileSync(path.join(dist, "theme-registry.js"), reExport); +fs.writeFileSync(path.join(dist, "theme-registry.d.ts"), reExportDts); + +console.log("@dangerousthings/web: built to dist/"); diff --git a/packages/web/src/base.css b/packages/web/src/base.css new file mode 100644 index 0000000..b0d4e8a --- /dev/null +++ b/packages/web/src/base.css @@ -0,0 +1,31 @@ +/* @dangerousthings/web: Base Tokens (shared across all brands) */ + +:root { + /* Spacing (4px base) */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-6: 1.5rem; + --space-8: 2rem; + + /* Transitions */ + --transition-fast: 150ms ease; + --transition: 250ms ease; + + /* Font stacks — brands override --font-heading and --font-body */ + --font-mono: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, monospace; + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-heading: var(--font-sans); + --font-body: var(--font-sans); + + /* Border Radius — brands override these */ + --radius-sm: 0.25rem; + --radius: 0.5rem; + --radius-lg: 0.75rem; + + /* Bevel sizes — DT sets real values; Classic/Supra leave at 0 */ + --bevel-sm: 0px; + --bevel-md: 0px; + --bevel-lg: 0px; +} diff --git a/packages/web/src/components/bevels.css b/packages/web/src/components/bevels.css new file mode 100644 index 0000000..439d261 --- /dev/null +++ b/packages/web/src/components/bevels.css @@ -0,0 +1,290 @@ +/* dt-web-theme: Bevel Utilities */ +/* Angular/beveled clip-path patterns from the DT design system */ +/* Source: dt-shopify-storefront clip-path patterns + react-native-dt-theme bevelPaths.ts */ +/* */ +/* clip-path clips CSS borders and box-shadow. This file uses the storefront's */ +/* dual-element technique: the element's own background serves as the "border" color, */ +/* and a ::before pseudo-element provides the inner surface fill. The gap between */ +/* them follows the clip-path shape perfectly. */ +/* */ +/* For glow effects on clipped elements, use filter: drop-shadow() (not box-shadow). */ + +/* ============================================================================ + Card Bevels — dual bottom bevels (bottom-right large + bottom-left small) + Source: storefront .card-container + .clip-corners-inner pattern + Outer bg = border color, ::before = surface fill + Border width: ~3px uniform (storefront --card-border-width: 0.2em) + ============================================================================ */ +[data-brand="dt"] .dt-bevel-card, +[data-brand="dt"] .card { + position: relative; + isolation: isolate; + background: var(--color-primary); + border: none; + border-radius: 0; + clip-path: polygon( + 0% 0%, + 100% 0%, + 100% calc(100% - var(--bevel-md)), + calc(100% - var(--bevel-md)) 100%, + var(--bevel-sm) 100%, + 0% calc(100% - var(--bevel-sm)) + ); +} + +[data-brand="dt"] .dt-bevel-card::before, +[data-brand="dt"] .card::before { + content: ''; + position: absolute; + inset: 0; + background: var(--color-surface); + z-index: -1; + /* Inner clip-path mirrors outer shape, inset 3px on all edges. + This creates a uniform cyan border along both straight edges AND bevel diagonals. */ + clip-path: polygon( + 3px 3px, + calc(100% - 3px) 3px, + calc(100% - 3px) calc(100% - var(--bevel-md)), + calc(100% - var(--bevel-md)) calc(100% - 3px), + var(--bevel-sm) calc(100% - 3px), + 3px calc(100% - var(--bevel-sm)) + ); +} + +/* Card Header — storefront .card-header pattern + Title extends edge-to-edge on the card's primary bg (cyan). + Below it, the ::before dark surface shows through for the body area. */ +[data-brand="dt"] .card > .card-title, +[data-brand="dt"] .dt-bevel-card > .card-title { + /* Pull title to card edges, canceling card padding */ + margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-4) calc(-1 * var(--space-6)); + padding: 1rem var(--space-6); + /* Sits above ::before so its own bg covers the dark surface */ + background: var(--color-primary); + color: var(--color-bg); + font-weight: 900; + text-transform: uppercase; + letter-spacing: 0.05em; + font-size: 1rem; + position: relative; +} + +/* Force icon spans to inherit header color (overrides inline styles) */ +[data-brand="dt"] .card > .card-title > * { + color: inherit !important; +} + +/* ============================================================================ + Button Bevels — top-right corner cut + Source: storefront .label-clipped pattern (solid fill + clip-path) + Filled buttons (primary/danger): direct clip-path, solid bg + Outline button (secondary): dual-element technique via ::before + ============================================================================ */ +[data-brand="dt"] .btn-primary, +[data-brand="dt"] .btn-danger { + border: none; + clip-path: polygon( + 0% 0%, + calc(100% - var(--bevel-sm)) 0%, + 100% var(--bevel-sm), + 100% 100%, + 0% 100% + ); +} + +[data-brand="dt"] .btn-secondary { + position: relative; + isolation: isolate; + background: var(--color-primary); + border: none; + clip-path: polygon( + 0% 0%, + calc(100% - var(--bevel-sm)) 0%, + 100% var(--bevel-sm), + 100% 100%, + 0% 100% + ); +} + +[data-brand="dt"] .btn-secondary::before { + content: ''; + position: absolute; + inset: 0; + background: var(--color-bg); + z-index: -1; + clip-path: polygon( + 2px 2px, + calc(100% - var(--bevel-sm)) 2px, + calc(100% - 2px) var(--bevel-sm), + calc(100% - 2px) calc(100% - 2px), + 2px calc(100% - 2px) + ); +} + +[data-brand="dt"] .btn-secondary:hover { + background: var(--color-accent); +} + +[data-brand="dt"] .btn-secondary:hover::before { + background: rgba(var(--color-primary-rgb), 0.1); +} + +/* ============================================================================ + Badge/Label Bevels — top-right bevel + Source: storefront .label-clipped pattern + Dual-element technique: badge bg = border color, ::before = inner fill + ============================================================================ */ +[data-brand="dt"] .badge { + --badge-border-color: var(--color-border); + --badge-fill: var(--color-surface); + position: relative; + isolation: isolate; + background: var(--badge-border-color); + color: var(--color-text-primary); + clip-path: polygon( + 0% 0%, + calc(100% - var(--bevel-sm)) 0%, + 100% var(--bevel-sm), + 100% 100%, + 0% 100% + ); + border: none; + border-radius: 0; +} + +[data-brand="dt"] .badge::before { + content: ''; + position: absolute; + inset: 0; + background: var(--badge-fill); + z-index: -1; + clip-path: polygon( + 2px 2px, + calc(100% - var(--bevel-sm)) 2px, + calc(100% - 2px) var(--bevel-sm), + calc(100% - 2px) calc(100% - 2px), + 2px calc(100% - 2px) + ); +} + +[data-brand="dt"] .badge-success { + --badge-border-color: var(--color-success); + --badge-fill: rgba(var(--color-success-rgb), 0.25); +} + +[data-brand="dt"] .badge-error { + --badge-border-color: var(--color-error); + --badge-fill: rgba(var(--color-error-rgb), 0.25); +} + +[data-brand="dt"] .badge-warning { + --badge-border-color: var(--color-warning); + --badge-fill: rgba(var(--color-warning-rgb), 0.25); +} + +[data-brand="dt"] .badge-info { + --badge-border-color: var(--color-info); + --badge-fill: rgba(var(--color-info-rgb), 0.25); +} + +/* ============================================================================ + Opt-in Bevel Utility Classes + Use these for custom elements that need bevel shapes + ============================================================================ */ +[data-brand="dt"] .dt-bevel-btn { + clip-path: polygon( + 0% 0%, + calc(100% - var(--bevel-sm)) 0%, + 100% var(--bevel-sm), + 100% 100%, + 0% 100% + ); +} + +[data-brand="dt"] .dt-bevel-label { + clip-path: polygon( + 0% 0%, + calc(100% - var(--bevel-sm)) 0%, + 100% var(--bevel-sm), + 100% 100%, + 0% 100% + ); + border-radius: 0; +} + +/* ============================================================================ + Media Frame Bevels — diagonal opposite corners (top-left + bottom-right) + Source: storefront .media-frame-clipped pattern + ============================================================================ */ +.dt-bevel-media { + clip-path: polygon( + var(--bevel-md) 0%, + 100% 0%, + 100% calc(100% - var(--bevel-md)), + calc(100% - var(--bevel-md)) 100%, + 0% 100%, + 0% var(--bevel-md) + ); +} + +/* ============================================================================ + Modal/Drawer Bevels — dual bottom bevels (bottom-right + bottom-left) + ============================================================================ */ +.dt-bevel-modal { + clip-path: polygon( + 0% 0%, + 100% 0%, + 100% calc(100% - var(--bevel-lg)), + calc(100% - var(--bevel-lg)) 100%, + var(--bevel-lg) 100%, + 0% calc(100% - var(--bevel-lg)) + ); +} + +/* ============================================================================ + Drawer Bevels — exposed-edge bevels + Source: storefront aside clip-path pattern + ============================================================================ */ +.dt-bevel-drawer-right { + clip-path: polygon( + 0% var(--bevel-lg), + var(--bevel-lg) 0%, + 100% 0%, + 100% 100%, + var(--bevel-lg) 100%, + 0% calc(100% - var(--bevel-lg)) + ); +} + +.dt-bevel-drawer-left { + clip-path: polygon( + 0% 0%, + calc(100% - var(--bevel-lg)) 0%, + 100% var(--bevel-lg), + 100% calc(100% - var(--bevel-lg)), + calc(100% - var(--bevel-lg)) 100%, + 0% 100% + ); +} + +/* ============================================================================ + Small Bevels — for compact elements (arrows, stepper buttons) + ============================================================================ */ +.dt-bevel-sm { + clip-path: polygon( + var(--bevel-sm) 0%, + 100% 0%, + 100% calc(100% - var(--bevel-sm)), + calc(100% - var(--bevel-sm)) 100%, + 0% 100%, + 0% var(--bevel-sm) + ); +} + +/* ============================================================================ + Accordion/Menu — thick top border accent (storefront menu-item-clipped) + ============================================================================ */ +[data-brand="dt"] .dt-accent-top, +[data-brand="dt"] .terminal { + border-top: 3px solid var(--color-primary); +} diff --git a/packages/web/src/components/elevation.css b/packages/web/src/components/elevation.css new file mode 100644 index 0000000..e4b7eef --- /dev/null +++ b/packages/web/src/components/elevation.css @@ -0,0 +1,96 @@ +/* dt-web-theme: Supra Elevation & MD3 Styles */ +/* Source: react-native-supra-theme/src/theme/paperTheme.ts */ +/* Rounded Material Design 3 aesthetic with elevation shadows */ + +/* ============================================================================ + Card Elevation + ============================================================================ */ +[data-brand="supra"] .card { + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18); + border-radius: var(--radius-lg); +} + +[data-brand="supra"] .card:hover { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.22); +} + +/* ============================================================================ + Button — Dual-layer inset shadow (hardware button feel) + Source: SupraButton.tsx — inner shadow for tactile appearance + ============================================================================ */ +[data-brand="supra"] .btn-primary { + box-shadow: + inset -2px -2px 4px rgba(0, 0, 0, 0.18), + inset 2px 2px 4px rgba(255, 255, 255, 0.25); + border-radius: var(--radius); +} + +[data-brand="supra"] .btn-primary:hover { + box-shadow: + inset -2px -2px 6px rgba(0, 0, 0, 0.22), + inset 2px 2px 6px rgba(255, 255, 255, 0.3); + transform: translateY(-1px); +} + +[data-brand="supra"] .btn-secondary { + border-radius: var(--radius); +} + +[data-brand="supra"] .btn-danger { + border-radius: var(--radius); +} + +/* ============================================================================ + Badge — Rounded pill (MD3 chip style) + ============================================================================ */ +[data-brand="supra"] .badge { + border-radius: 8px; +} + +/* ============================================================================ + Input — Rounded with standard focus ring + ============================================================================ */ +[data-brand="supra"] .input, +[data-brand="supra"] input, +[data-brand="supra"] textarea, +[data-brand="supra"] select { + border-radius: var(--radius); +} + +[data-brand="supra"] .input:focus, +[data-brand="supra"] input:focus, +[data-brand="supra"] textarea:focus, +[data-brand="supra"] select:focus { + box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.2); +} + +/* ============================================================================ + Terminal — Rounded with subtle elevation + ============================================================================ */ +[data-brand="supra"] .terminal { + border-radius: var(--radius); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18); +} + +/* ============================================================================ + Elevation Utility Classes + ============================================================================ */ +.supra-elevation-1 { + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); +} + +.supra-elevation-2 { + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18); +} + +.supra-elevation-3 { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.22); +} + +.supra-elevation-4 { + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25); +} + +.supra-elevation-5 { + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); +} diff --git a/packages/web/src/components/forms-dt.css b/packages/web/src/components/forms-dt.css new file mode 100644 index 0000000..2c35547 --- /dev/null +++ b/packages/web/src/components/forms-dt.css @@ -0,0 +1,329 @@ +/* dt-web-theme: DT Form Component Styles */ +/* Source: react-native-dt-theme/src/components/DT*.tsx */ +/* These styles exist in the RN DT theme but NOT the Shopify storefront */ + +/* ============================================================================ + Text Input — Sharp corners + focus glow bar + Source: DTTextInput.tsx + ============================================================================ */ +[data-brand="dt"] input[type="text"], +[data-brand="dt"] input[type="password"], +[data-brand="dt"] input[type="email"], +[data-brand="dt"] input[type="number"], +[data-brand="dt"] input[type="search"], +[data-brand="dt"] input[type="url"], +[data-brand="dt"] input[type="tel"], +[data-brand="dt"] textarea, +[data-brand="dt"] select, +[data-brand="dt"] .input { + border: 2px solid var(--color-primary); + border-radius: 0; + background: var(--color-bg); + color: var(--color-text-primary); + transition: border-color var(--transition-fast), box-shadow var(--transition-fast); +} + +[data-brand="dt"] input:focus, +[data-brand="dt"] textarea:focus, +[data-brand="dt"] select:focus, +[data-brand="dt"] .input:focus { + outline: none; + border-color: var(--color-primary); + box-shadow: 0 4px 0 1px var(--color-primary); +} + +[data-brand="dt"] input.error, +[data-brand="dt"] .input.error { + border-color: var(--color-error); +} + +[data-brand="dt"] input.error:focus, +[data-brand="dt"] .input.error:focus { + box-shadow: 0 4px 0 1px var(--color-error); +} + +/* ============================================================================ + Checkbox — Beveled opposing corners (top-left + bottom-right) + Source: DTCheckbox.tsx — bevel at 30% of size + Uses drop-shadow instead of border (clip-path clips CSS borders) + ============================================================================ */ +[data-brand="dt"] input[type="checkbox"] { + appearance: none; + -webkit-appearance: none; + width: 24px; + height: 24px; + border: none; + background: rgba(var(--color-primary-rgb), 0.1); + cursor: pointer; + position: relative; + clip-path: polygon( + 30% 0%, 100% 0%, 100% 70%, 70% 100%, 0% 100%, 0% 30% + ); + filter: drop-shadow(0 0 1px var(--color-primary)); + transition: background var(--transition-fast), filter var(--transition-fast); +} + +[data-brand="dt"] input[type="checkbox"]:checked { + background: var(--color-primary); + filter: drop-shadow(0 0 2px var(--color-primary)); +} + +[data-brand="dt"] input[type="checkbox"]:checked::after { + content: ''; + position: absolute; + left: 6px; + top: 8px; + width: 10px; + height: 6px; + border: 2px solid var(--color-bg); + border-top: none; + border-right: none; + transform: rotate(-45deg); +} + +[data-brand="dt"] input[type="checkbox"]:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ============================================================================ + Switch / Toggle — Angular track + sliding square thumb + Source: DTSwitch.tsx — 48x26 track, 20x20 thumb, 150ms + ============================================================================ */ +.dt-switch { + position: relative; + display: inline-block; + width: 48px; + height: 26px; + cursor: pointer; +} + +.dt-switch input { + opacity: 0; + width: 0; + height: 0; + position: absolute; +} + +.dt-switch-track { + position: absolute; + inset: 0; + border: 2px solid var(--color-primary); + border-radius: 0; + background: rgba(var(--color-primary-rgb), 0.1); + transition: background var(--transition-fast); +} + +.dt-switch input:checked + .dt-switch-track { + background: var(--color-primary); +} + +.dt-switch-thumb { + position: absolute; + top: 3px; + left: 3px; + width: 20px; + height: 20px; + background: #ffffff; + transition: left var(--transition-fast); +} + +.dt-switch input:checked ~ .dt-switch-thumb, +.dt-switch input:checked + .dt-switch-track + .dt-switch-thumb { + left: 25px; + background: var(--color-bg); +} + +.dt-switch input:disabled + .dt-switch-track { + opacity: 0.5; + cursor: not-allowed; +} + +/* ============================================================================ + Radio Button — Hexagonal indicator + Source: DTRadioGroup.tsx — flat-top hexagon, 22px default + Uses drop-shadow instead of border (clip-path clips CSS borders) + ============================================================================ */ +[data-brand="dt"] input[type="radio"] { + appearance: none; + -webkit-appearance: none; + width: 22px; + height: 22px; + border: none; + background: rgba(var(--color-primary-rgb), 0.1); + cursor: pointer; + clip-path: polygon( + 50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25% + ); + filter: drop-shadow(0 0 1px var(--color-primary)); + transition: background var(--transition-fast), filter var(--transition-fast); +} + +[data-brand="dt"] input[type="radio"]:checked { + background: var(--color-primary); + filter: drop-shadow(0 0 2px var(--color-primary)); +} + +[data-brand="dt"] input[type="radio"]:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Radio group option container */ +.dt-radio-option { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + border: 2px solid rgba(var(--color-primary-rgb), 0.3); + cursor: pointer; + transition: all var(--transition-fast); +} + +.dt-radio-option.selected { + background: rgba(var(--color-primary-rgb), 0.15); + border-color: var(--color-primary); +} + +/* ============================================================================ + Progress Bar — Angular, no radius + Source: DTProgressBar.tsx — 4px height, no border-radius + ============================================================================ */ +.dt-progress { + width: 100%; + height: 4px; + background: rgba(255, 255, 255, 0.1); + border-radius: 0; + overflow: hidden; +} + +.dt-progress-fill { + height: 100%; + background: var(--color-primary); + transition: width 300ms ease-out; +} + +.dt-progress-label { + font-size: 11px; + font-weight: 600; + color: var(--color-primary); + margin-top: 4px; + font-family: var(--font-mono); +} + +/* Vertical variant */ +.dt-progress.vertical { + width: 8px; + height: 200px; + display: flex; + flex-direction: column-reverse; +} + +.dt-progress.vertical .dt-progress-fill { + width: 100%; + height: 0%; + transition: height 300ms ease-out; +} + +/* ============================================================================ + Accordion — 5px top border, chevron rotation + Source: DTAccordion.tsx — 250ms animation + ============================================================================ */ +.dt-accordion { + display: flex; + flex-direction: column; + gap: 4px; +} + +.dt-accordion-header { + background: var(--color-bg); + border: 1px solid var(--color-primary); + border-top: 5px solid var(--color-primary); + padding: 12px 16px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + text-transform: uppercase; + font-weight: 600; + font-size: 0.875rem; + letter-spacing: 0.05em; + color: var(--color-text-primary); + transition: opacity var(--transition-fast); +} + +.dt-accordion-header:hover { + opacity: 0.7; +} + +.dt-accordion-header[aria-expanded="true"] { + color: var(--color-secondary); + border-top-color: var(--color-secondary); +} + +.dt-accordion-chevron { + display: inline-block; + transition: transform 250ms ease-in-out; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 7px solid currentColor; +} + +.dt-accordion-header[aria-expanded="true"] .dt-accordion-chevron { + transform: rotate(180deg); +} + +.dt-accordion-content { + max-height: 0; + overflow: hidden; + transition: max-height 250ms ease-in-out; +} + +.dt-accordion-content[data-open="true"] { + max-height: 2000px; +} + +/* ============================================================================ + Quantity Stepper — Beveled +/- buttons + Source: DTQuantityStepper.tsx — bottom-right bevel + ============================================================================ */ +.dt-stepper { + display: inline-flex; + gap: 4px; + align-items: center; +} + +.dt-stepper-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border: 2px solid var(--color-primary); + background: transparent; + color: var(--color-primary); + font-size: 1.25rem; + font-weight: bold; + cursor: pointer; + border-radius: 0; + transition: opacity var(--transition-fast); +} + +.dt-stepper-btn:hover { + opacity: 0.7; +} + +.dt-stepper-btn:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.dt-stepper-value { + min-width: 36px; + text-align: center; + color: var(--color-primary); + font-weight: 700; + font-family: var(--font-mono); +} diff --git a/packages/web/src/components/glows.css b/packages/web/src/components/glows.css new file mode 100644 index 0000000..758f5fe --- /dev/null +++ b/packages/web/src/components/glows.css @@ -0,0 +1,80 @@ +/* dt-web-theme: Glow & Neon Effects */ +/* Source: dt-shopify-storefront glow patterns */ +/* */ +/* Elements with clip-path use filter: drop-shadow() instead of box-shadow */ +/* because clip-path clips box-shadow but filter follows the clipped shape. */ + +/* ============================================================================ + Button Glows — filter: drop-shadow (buttons have clip-path) + ============================================================================ */ +[data-brand="dt"] .btn-primary { + box-shadow: none; + filter: drop-shadow(0 0 10px rgba(var(--color-primary-rgb), 0.3)); +} + +[data-brand="dt"] .btn-primary:hover { + box-shadow: none; + filter: drop-shadow(0 0 15px rgba(var(--color-accent-rgb), 0.4)); +} + +[data-brand="dt"] .btn-secondary:hover { + filter: drop-shadow(0 0 8px rgba(var(--color-primary-rgb), 0.3)); +} + +[data-brand="dt"] .btn-danger { + box-shadow: none; + filter: drop-shadow(0 0 10px rgba(var(--color-error-rgb), 0.3)); +} + +[data-brand="dt"] .btn-danger:hover { + box-shadow: none; + filter: drop-shadow(0 0 15px rgba(var(--color-error-rgb), 0.4)); +} + +/* ============================================================================ + Link Glow + ============================================================================ */ +[data-brand="dt"] a:hover { + text-shadow: 0 0 8px var(--color-primary); +} + +/* ============================================================================ + Terminal Inset Glow (no clip-path, box-shadow OK) + ============================================================================ */ +[data-brand="dt"] .terminal { + box-shadow: inset 0 0 10px rgba(var(--color-accent-rgb), 0.05), + 0 0 10px rgba(var(--color-accent-rgb), 0.1); +} + +/* ============================================================================ + Card Hover Glow — filter: drop-shadow (cards have clip-path) + ============================================================================ */ +[data-brand="dt"] .card:hover { + filter: drop-shadow(0 0 8px rgba(var(--color-primary-rgb), 0.3)); +} + +/* ============================================================================ + Input Focus Glow (no clip-path on inputs, box-shadow OK) + ============================================================================ */ +[data-brand="dt"] .input:focus { + box-shadow: 0 4px 0 1px var(--color-primary); +} + +/* ============================================================================ + Generic Glow Utilities + ============================================================================ */ +.dt-glow { + filter: drop-shadow(0 0 10px rgba(var(--color-primary-rgb), 0.3)); +} + +.dt-glow-strong { + filter: drop-shadow(0 0 15px rgba(var(--color-primary-rgb), 0.5)); +} + +.dt-glow-inset { + box-shadow: inset 0 0 10px rgba(var(--color-primary-rgb), 0.1); +} + +.dt-text-glow { + text-shadow: 0 0 8px currentColor; +} diff --git a/packages/web/src/dt-font.css b/packages/web/src/dt-font.css new file mode 100644 index 0000000..79a4e32 --- /dev/null +++ b/packages/web/src/dt-font.css @@ -0,0 +1,9 @@ +/* @dangerousthings/web: Tektur Font Face */ +@font-face { + font-family: 'Tektur'; + src: url('../fonts/Tektur-VariableFont_wdth,wght.ttf') format('truetype'); + font-weight: 100 900; + font-stretch: 75% 125%; + font-style: normal; + font-display: swap; +} diff --git a/packages/web/src/index.css b/packages/web/src/index.css new file mode 100644 index 0000000..bc84266 --- /dev/null +++ b/packages/web/src/index.css @@ -0,0 +1,21 @@ +/* @dangerousthings/web: Main Entry Point */ +/* Import this file to get all tokens and component styles */ + +/* Base tokens (spacing, transitions, font stacks) */ +@import './base.css'; + +/* Tektur font face (used by DT brand) */ +@import './dt-font.css'; + +/* Brand tokens — generated from @dangerousthings/tokens */ +@import './tokens/dt.css'; +@import './tokens/classic.css'; +@import './tokens/supra.css'; + +/* Component styles — DT visual identity */ +@import './components/bevels.css'; +@import './components/glows.css'; +@import './components/forms-dt.css'; + +/* Component styles — Supra/MD3 visual identity */ +@import './components/elevation.css'; diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..5673e2c --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true + } +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..d35f080 --- /dev/null +++ b/turbo.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "clean": { + "cache": false + }, + "typecheck": { + "dependsOn": ["^build"] + }, + "lint": {} + } +}