From f1e04d36a65be60a94d7e099b9a3556695a8fe43 Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Thu, 3 Sep 2026 21:17:02 +0200 Subject: [PATCH 01/18] feat(native): add cross-platform renderer foundation --- .github/workflows/ci.yml | 19 + .github/workflows/native-canary.yml | 272 + .gitignore | 1 + CHANGELOG.md | 1 + CONTRIBUTING.md | 14 +- README.md | 29 +- ROADMAP.md | 40 +- apps/native-catalog/App.test.tsx | 17 + apps/native-catalog/App.tsx | 79 + apps/native-catalog/README.md | 10 + apps/native-catalog/app.json | 16 + apps/native-catalog/babel.config.cjs | 3 + apps/native-catalog/catalog-sections.tsx | 183 + apps/native-catalog/eslint.config.js | 28 + apps/native-catalog/index.ts | 5 + apps/native-catalog/jest.config.cjs | 12 + apps/native-catalog/metro.config.cjs | 3 + apps/native-catalog/package.json | 52 + apps/native-catalog/tsconfig.json | 8 + .../app/[locale]/components/[slug]/page.tsx | 36 +- .../registry/app/[locale]/components/page.tsx | 92 +- apps/registry/app/llms-full.txt/route.ts | 18 +- apps/registry/app/llms.txt/route.ts | 16 +- apps/registry/app/mcp/route.test.ts | 39 + apps/registry/app/mcp/route.ts | 35 +- apps/registry/app/r/registry.json/route.ts | 4 +- .../component-card/component-card.tsx | 6 + apps/registry/components/header/header.tsx | 9 +- .../components/platform-badges/index.ts | 1 + .../platform-badges/platform-badges.tsx | 31 + .../registry/content/pages/docs/agents/en.mdx | 4 +- .../registry/content/pages/docs/agents/fr.mdx | 4 +- .../registry/content/pages/docs/native/en.mdx | 84 + .../registry/content/pages/docs/native/fr.mdx | 84 + .../content/pages/docs/registry/en.mdx | 6 +- .../content/pages/docs/registry/fr.mdx | 6 +- apps/registry/e2e/i18n.spec.ts | 1 + apps/registry/e2e/platforms.spec.ts | 60 + apps/registry/lib/component-metadata.json | 944 +++ apps/registry/lib/docs-pages.ts | 6 + apps/registry/lib/jsonld.test.ts | 14 + apps/registry/lib/jsonld.ts | 9 +- apps/registry/lib/portable-contracts.test.ts | 107 + apps/registry/lib/registry.test.ts | 62 + apps/registry/lib/registry.ts | 67 +- apps/registry/messages/en.json | 11 + apps/registry/messages/fr.json | 11 + apps/registry/package.json | 1 + apps/registry/registry.json | 1596 +++- apps/registry/registry.ts | 2 +- .../scripts/check-registry-integrity.ts | 63 +- .../scripts/generate-component-metadata.ts | 3 + .../scripts/inline-component-source.ts | 61 +- .../scripts/stamp-registry-metadata.ts | 41 +- apps/registry/types/registry.ts | 1 + docs/ARCHITECTURE.md | 140 +- docs/RELEASING.md | 25 +- doctor.config.json | 3 +- package.json | 7 +- packages/design/README.md | 21 +- packages/design/component-contracts.json | 38 + .../design/component-contracts.schema.json | 76 + packages/design/package.json | 13 + packages/design/scripts/generate-tokens.mjs | 409 + packages/design/tokens.json | 33 +- packages/design/tokens.schema.json | 164 +- packages/ui-core/CHANGELOG.md | 9 + packages/ui-core/README.md | 29 + packages/ui-core/component-contracts.json | 38 + .../ui-core/component-contracts.schema.json | 76 + packages/ui-core/eslint.config.js | 21 + packages/ui-core/package.json | 75 + .../ui-core/scripts/check-packed-package.mjs | 87 + .../ui-core/src/generated/design-tokens.ts | 495 ++ packages/ui-core/src/index.ts | 27 + packages/ui-core/src/platform.ts | 12 + packages/ui-core/src/theme.test.ts | 101 + packages/ui-core/src/theme.ts | 96 + packages/ui-core/tokens.json | 278 + packages/ui-core/tokens.schema.json | 162 + packages/ui-core/tsconfig.build.json | 11 + packages/ui-core/tsconfig.json | 13 + packages/ui-core/tsup.config.ts | 12 + packages/ui-core/vitest.config.ts | 9 + packages/ui-native/CHANGELOG.md | 9 + packages/ui-native/README.md | 36 + packages/ui-native/babel.config.cjs | 3 + packages/ui-native/eslint.config.js | 31 + packages/ui-native/jest.config.cjs | 11 + packages/ui-native/package.json | 91 + packages/ui-native/registry.json | 13 + packages/ui-native/registry.schema.json | 27 + .../ui-native/scripts/check-boundaries.mjs | 44 + .../scripts/check-packed-package.mjs | 82 + .../ui-native/src/components/badge/badge.tsx | 117 + .../src/components/button/button-styles.ts | 98 + .../src/components/button/button.tsx | 97 + .../ui-native/src/components/card/card.tsx | 158 + .../src/components/components.test.tsx | 93 + .../src/components/heading/heading.tsx | 52 + .../ui-native/src/components/text/text.tsx | 62 + packages/ui-native/src/index.ts | 37 + .../ui-native/src/theme/theme-provider.tsx | 53 + packages/ui-native/tsconfig.build.json | 12 + packages/ui-native/tsconfig.json | 14 + packages/ui-native/tsup.config.ts | 13 + pnpm-lock.yaml | 7175 +++++++++++++++-- 107 files changed, 13919 insertions(+), 1216 deletions(-) create mode 100644 .github/workflows/native-canary.yml create mode 100644 apps/native-catalog/App.test.tsx create mode 100644 apps/native-catalog/App.tsx create mode 100644 apps/native-catalog/README.md create mode 100644 apps/native-catalog/app.json create mode 100644 apps/native-catalog/babel.config.cjs create mode 100644 apps/native-catalog/catalog-sections.tsx create mode 100644 apps/native-catalog/eslint.config.js create mode 100644 apps/native-catalog/index.ts create mode 100644 apps/native-catalog/jest.config.cjs create mode 100644 apps/native-catalog/metro.config.cjs create mode 100644 apps/native-catalog/package.json create mode 100644 apps/native-catalog/tsconfig.json create mode 100644 apps/registry/app/mcp/route.test.ts create mode 100644 apps/registry/components/platform-badges/index.ts create mode 100644 apps/registry/components/platform-badges/platform-badges.tsx create mode 100644 apps/registry/content/pages/docs/native/en.mdx create mode 100644 apps/registry/content/pages/docs/native/fr.mdx create mode 100644 apps/registry/e2e/platforms.spec.ts create mode 100644 apps/registry/lib/portable-contracts.test.ts create mode 100644 apps/registry/lib/registry.test.ts create mode 100644 packages/design/component-contracts.json create mode 100644 packages/design/component-contracts.schema.json create mode 100644 packages/design/package.json create mode 100644 packages/design/scripts/generate-tokens.mjs create mode 100644 packages/ui-core/CHANGELOG.md create mode 100644 packages/ui-core/README.md create mode 100644 packages/ui-core/component-contracts.json create mode 100644 packages/ui-core/component-contracts.schema.json create mode 100644 packages/ui-core/eslint.config.js create mode 100644 packages/ui-core/package.json create mode 100644 packages/ui-core/scripts/check-packed-package.mjs create mode 100644 packages/ui-core/src/generated/design-tokens.ts create mode 100644 packages/ui-core/src/index.ts create mode 100644 packages/ui-core/src/platform.ts create mode 100644 packages/ui-core/src/theme.test.ts create mode 100644 packages/ui-core/src/theme.ts create mode 100644 packages/ui-core/tokens.json create mode 100644 packages/ui-core/tokens.schema.json create mode 100644 packages/ui-core/tsconfig.build.json create mode 100644 packages/ui-core/tsconfig.json create mode 100644 packages/ui-core/tsup.config.ts create mode 100644 packages/ui-core/vitest.config.ts create mode 100644 packages/ui-native/CHANGELOG.md create mode 100644 packages/ui-native/README.md create mode 100644 packages/ui-native/babel.config.cjs create mode 100644 packages/ui-native/eslint.config.js create mode 100644 packages/ui-native/jest.config.cjs create mode 100644 packages/ui-native/package.json create mode 100644 packages/ui-native/registry.json create mode 100644 packages/ui-native/registry.schema.json create mode 100644 packages/ui-native/scripts/check-boundaries.mjs create mode 100644 packages/ui-native/scripts/check-packed-package.mjs create mode 100644 packages/ui-native/src/components/badge/badge.tsx create mode 100644 packages/ui-native/src/components/button/button-styles.ts create mode 100644 packages/ui-native/src/components/button/button.tsx create mode 100644 packages/ui-native/src/components/card/card.tsx create mode 100644 packages/ui-native/src/components/components.test.tsx create mode 100644 packages/ui-native/src/components/heading/heading.tsx create mode 100644 packages/ui-native/src/components/text/text.tsx create mode 100644 packages/ui-native/src/index.ts create mode 100644 packages/ui-native/src/theme/theme-provider.tsx create mode 100644 packages/ui-native/tsconfig.build.json create mode 100644 packages/ui-native/tsconfig.json create mode 100644 packages/ui-native/tsup.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a96a9a7..89d4e53c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,25 @@ jobs: - name: Verify component previews resolve to real stories run: pnpm -F @vllnt/ui-registry registry:verify-previews + native: + name: Native Package Gates + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - uses: actions/checkout@v5 + + - uses: pnpm/action-setup@v5 + + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Verify native packages and Expo bundles + run: pnpm ci:native + e2e: name: E2E (Playwright) runs-on: ubuntu-latest diff --git a/.github/workflows/native-canary.yml b/.github/workflows/native-canary.yml new file mode 100644 index 00000000..e59d28aa --- /dev/null +++ b/.github/workflows/native-canary.yml @@ -0,0 +1,272 @@ +name: Native Canary + +on: + push: + branches: [main] + paths: + - "packages/design/**" + - "packages/ui-core/**" + - "packages/ui-native/**" + - "apps/native-catalog/**" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - "turbo.json" + - ".github/workflows/native-canary.yml" + +concurrency: + group: native-canary-main + cancel-in-progress: false + +permissions: + contents: read + +jobs: + quality: + name: Native Quality Gates + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - uses: actions/checkout@v5 + + - uses: pnpm/action-setup@v5 + + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - run: pnpm ci:native + + publish: + name: Publish Native Canary Pair + if: vars.NATIVE_CANARY_PUBLISH_ENABLED == 'true' + needs: quality + runs-on: ubuntu-latest + timeout-minutes: 20 + environment: npm-native-canary + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v5 + + - uses: pnpm/action-setup@v5 + + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: pnpm + registry-url: https://registry.npmjs.org + + - run: pnpm install --frozen-lockfile + + - name: Build publication artifacts + run: pnpm --filter '@vllnt/ui-native...' build + + - name: Publish synchronized canaries + shell: bash + run: | + set -euo pipefail + + CORE_DIR="packages/ui-core" + NATIVE_DIR="packages/ui-native" + + test "$(node -p "require('./${CORE_DIR}/package.json').name")" = "@vllnt/ui-core" + test "$(node -p "require('./${NATIVE_DIR}/package.json').name")" = "@vllnt/ui-native" + + CORE_BASE="$(node -p "require('./${CORE_DIR}/package.json').version")" + NATIVE_BASE="$(node -p "require('./${NATIVE_DIR}/package.json').version")" + test "$CORE_BASE" = "$NATIVE_BASE" + case "$CORE_BASE" in + *-*) echo "::error::Base package versions must not be prereleases"; exit 1 ;; + esac + + current_main_sha() { + git ls-remote --exit-code origin refs/heads/main | awk '{print $1}' + } + REMOTE_MAIN_SHA="$(current_main_sha)" + test -n "$REMOTE_MAIN_SHA" + if [[ "$REMOTE_MAIN_SHA" != "$GITHUB_SHA" ]]; then + echo "::notice::Skipping superseded canary run for ${GITHUB_SHA}" + exit 0 + fi + + SHORT_SHA="$(printf '%s' "$GITHUB_SHA" | cut -c1-12)" + CANARY_VERSION="${CORE_BASE}-canary.${GITHUB_RUN_NUMBER}.sha${SHORT_SHA}" + STAGING_TAG="run-${GITHUB_RUN_ID}" + + read_tags() { + local package="$1" + local output_file error_file output + output_file="$(mktemp "$RUNNER_TEMP/npm-view-output.XXXXXX")" + error_file="$(mktemp "$RUNNER_TEMP/npm-view-error.XXXXXX")" + if npm view "$package" dist-tags --json >"$output_file" 2>"$error_file"; then + output="$(cat "$output_file")" + rm -f "$output_file" "$error_file" + [[ -n "$output" ]] || output='{}' + if ! jq -e 'type == "object"' <<<"$output" >/dev/null; then + echo "::error::npm returned invalid dist-tag data for ${package}" >&2 + return 1 + fi + printf '%s\n' "$output" + return 0 + fi + if grep -q 'E404' "$output_file" "$error_file"; then + rm -f "$output_file" "$error_file" + printf '{}\n' + return 0 + fi + cat "$output_file" "$error_file" >&2 + rm -f "$output_file" "$error_file" + return 1 + } + tag_value() { + jq -r --arg tag "$2" '.[$tag] // ""' <<<"$1" + } + + CORE_TAGS_BEFORE="$(read_tags @vllnt/ui-core)" + NATIVE_TAGS_BEFORE="$(read_tags @vllnt/ui-native)" + CORE_LATEST_BEFORE="$(tag_value "$CORE_TAGS_BEFORE" latest)" + NATIVE_LATEST_BEFORE="$(tag_value "$NATIVE_TAGS_BEFORE" latest)" + CORE_CANARY_BEFORE="$(tag_value "$CORE_TAGS_BEFORE" canary)" + NATIVE_CANARY_BEFORE="$(tag_value "$NATIVE_TAGS_BEFORE" canary)" + + npm version "$CANARY_VERSION" --prefix "$CORE_DIR" --no-git-tag-version --ignore-scripts + npm version "$CANARY_VERSION" --prefix "$NATIVE_DIR" --no-git-tag-version --ignore-scripts + node - "$NATIVE_DIR/package.json" "$CANARY_VERSION" <<'NODE' + const fs = require("node:fs"); + const [manifestPath, version] = process.argv.slice(2); + const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + manifest.dependencies["@vllnt/ui-core"] = version; + fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); + NODE + + PACK_DIR="$RUNNER_TEMP/native-packs" + mkdir -p "$PACK_DIR" + CORE_TARBALL="$(pnpm --dir "$CORE_DIR" pack --pack-destination "$PACK_DIR" | tail -n1)" + NATIVE_TARBALL="$(pnpm --dir "$NATIVE_DIR" pack --pack-destination "$PACK_DIR" | tail -n1)" + + test "$(tar -xOf "$CORE_TARBALL" package/package.json | jq -r '.name')" = "@vllnt/ui-core" + test "$(tar -xOf "$CORE_TARBALL" package/package.json | jq -r '.version')" = "$CANARY_VERSION" + test "$(tar -xOf "$NATIVE_TARBALL" package/package.json | jq -r '.name')" = "@vllnt/ui-native" + test "$(tar -xOf "$NATIVE_TARBALL" package/package.json | jq -r '.version')" = "$CANARY_VERSION" + test "$(tar -xOf "$NATIVE_TARBALL" package/package.json | jq -r '.["react-native"]')" = "./dist/index.js" + tar -tf "$NATIVE_TARBALL" | grep -qx 'package/dist/index.js' + test "$(tar -xOf "$NATIVE_TARBALL" package/package.json | jq -r '.dependencies["@vllnt/ui-core"]')" = "$CANARY_VERSION" + if tar -xOf "$NATIVE_TARBALL" package/package.json | grep -q 'workspace:'; then + echo "::error::Packed native manifest still contains a workspace protocol" + exit 1 + fi + + sed -i '/_authToken/d' "$NPM_CONFIG_USERCONFIG" + unset NODE_AUTH_TOKEN + + PROMOTION_STARTED=false + remove_owned_tag() { + local package="$1" + local tag="$2" + local expected="$3" + local tags current + tags="$(read_tags "$package")" || return 1 + current="$(tag_value "$tags" "$tag")" + [[ -n "$current" ]] || return 0 + if [[ "$current" != "$expected" ]]; then + echo "::error::Refusing to remove ${package} tag ${tag}: expected ${expected}, found ${current}" + return 1 + fi + npx --yes npm@11.18.0 dist-tag rm "$package" "$tag" || return 1 + tags="$(read_tags "$package")" || return 1 + [[ -z "$(tag_value "$tags" "$tag")" ]] + } + cleanup_staging() { + local failed=0 + remove_owned_tag @vllnt/ui-core "$STAGING_TAG" "$CANARY_VERSION" || failed=1 + remove_owned_tag @vllnt/ui-native "$STAGING_TAG" "$CANARY_VERSION" || failed=1 + return "$failed" + } + on_exit() { + local status=$? + local recovery_failed=0 + trap - EXIT + if [[ $status -ne 0 && "$PROMOTION_STARTED" = true ]]; then + restore_pair || recovery_failed=1 + fi + cleanup_staging || recovery_failed=1 + if [[ $recovery_failed -ne 0 ]]; then + echo "::error::Could not fully restore npm dist-tags" + [[ $status -ne 0 ]] || status=1 + fi + exit "$status" + } + trap on_exit EXIT + + if ! npm view "@vllnt/ui-core@${CANARY_VERSION}" version >/dev/null 2>&1; then + npx --yes npm@11.18.0 publish "$CORE_TARBALL" --tag "$STAGING_TAG" --provenance --access public + fi + + for attempt in $(seq 1 12); do + npm view "@vllnt/ui-core@${CANARY_VERSION}" version >/dev/null 2>&1 && break + sleep 5 + done + npm view "@vllnt/ui-core@${CANARY_VERSION}" version >/dev/null + + if ! npm view "@vllnt/ui-native@${CANARY_VERSION}" version >/dev/null 2>&1; then + npx --yes npm@11.18.0 publish "$NATIVE_TARBALL" --tag "$STAGING_TAG" --provenance --access public + fi + + for attempt in $(seq 1 12); do + npm view "@vllnt/ui-native@${CANARY_VERSION}" version >/dev/null 2>&1 && break + sleep 5 + done + npm view "@vllnt/ui-native@${CANARY_VERSION}" version >/dev/null + + REMOTE_MAIN_SHA="$(current_main_sha)" + test -n "$REMOTE_MAIN_SHA" + if [[ "$REMOTE_MAIN_SHA" != "$GITHUB_SHA" ]]; then + echo "::notice::Skipping canary promotion because main advanced to ${REMOTE_MAIN_SHA}" + exit 0 + fi + + restore_tag() { + local package="$1" + local previous="$2" + local tags current + tags="$(read_tags "$package")" || return 1 + current="$(tag_value "$tags" canary)" + [[ "$current" = "$previous" ]] && return 0 + if [[ -n "$current" && "$current" != "$CANARY_VERSION" ]]; then + echo "::error::Refusing to overwrite unexpected ${package} canary ${current}" + return 1 + fi + if [[ -n "$previous" ]]; then + npx --yes npm@11.18.0 dist-tag add "${package}@${previous}" canary || return 1 + else + remove_owned_tag "$package" canary "$CANARY_VERSION" || return 1 + fi + tags="$(read_tags "$package")" || return 1 + [[ "$(tag_value "$tags" canary)" = "$previous" ]] + } + + restore_pair() { + local failed=0 + restore_tag @vllnt/ui-core "$CORE_CANARY_BEFORE" || failed=1 + restore_tag @vllnt/ui-native "$NATIVE_CANARY_BEFORE" || failed=1 + return "$failed" + } + + PROMOTION_STARTED=true + npx --yes npm@11.18.0 dist-tag add "@vllnt/ui-core@${CANARY_VERSION}" canary + npx --yes npm@11.18.0 dist-tag add "@vllnt/ui-native@${CANARY_VERSION}" canary + + CORE_TAGS_AFTER="$(read_tags @vllnt/ui-core)" + NATIVE_TAGS_AFTER="$(read_tags @vllnt/ui-native)" + test "$(tag_value "$CORE_TAGS_AFTER" canary)" = "$CANARY_VERSION" + test "$(tag_value "$NATIVE_TAGS_AFTER" canary)" = "$CANARY_VERSION" + test "$(tag_value "$CORE_TAGS_AFTER" latest)" = "$CORE_LATEST_BEFORE" + test "$(tag_value "$NATIVE_TAGS_AFTER" latest)" = "$NATIVE_LATEST_BEFORE" + + PROMOTION_STARTED=false + exit 0 diff --git a/.gitignore b/.gitignore index 64433312..eeab1a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ # Build outputs .next/ +.expo/ dist/ .turbo/ coverage/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 8480603f..b1f99a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Release automation can regenerate this file from Conventional Commits with ### Added +- **Cross-platform foundation** — added the framework-free `@vllnt/ui-core` token/contract package, an experimental canary-only `@vllnt/ui-native` renderer with Button, Text, Heading, Badge, and Card, plus a private Expo catalog that validates Android and iOS Metro bundles. The registry, component pages, search, llms surfaces, JSON-LD, and MCP now expose web/native availability. Existing `@vllnt/ui` exports and stable publishing remain unchanged. (#479) - **Component family landing pages** - every component family has a standalone, SEO-oriented landing at `/families/[category]`, plus a `/families` index. One shared template renders a hero with CTAs, per-family SEO sub-groups with diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e60787b..4d508b4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,8 @@ Key scripts (from repo root): | `pnpm -F @vllnt/ui test:visual` | Playwright CT visual snapshots | | `pnpm check:circular` | Fail on circular imports | | `pnpm doctor` | react-doctor React-health scan | +| `pnpm tokens:check` | Verify generated token artifacts | +| `pnpm ci:native` | Verify core/native packages and Expo bundles | A [react-doctor](https://github.com/millionco/react-doctor) **pre-commit hook** (in `.githooks/`, enabled automatically on `pnpm install`) blocks commits that @@ -58,7 +60,7 @@ with `git commit --no-verify`. See AGENTS.md → *React health* for details. ``` 2. Follow the existing patterns: - - `React.forwardRef` on every component. + - React 19 ref-as-prop support and `displayName` on every named component. - `cn()` from `src/lib/utils.ts` for class merging. - Radix primitives for accessible behavior where applicable. - CVA for variants (`class-variance-authority`). @@ -87,15 +89,9 @@ with `git commit --no-verify`. See AGENTS.md → *React health* for details. ## Releases -Releases are cut via `workflow_dispatch` on `.github/workflows/publish.yml`. Maintainers pick `patch` / `minor` / `major` and the workflow: +Stable `@vllnt/ui` versions are prepared in a normal version-bump PR. A maintainer then dispatches `.github/workflows/publish.yml` from `main`; the workflow validates the pre-bumped version, publishes with OIDC-signed provenance, tags it, and creates the GitHub release. Web canaries publish automatically after pushes to `main`. -1. Bumps `packages/ui/package.json`. -2. Generates release notes from commits. -3. Pushes an annotated tag `v{x.y.z}` back to `main`. -4. Publishes to the public npm registry with OIDC-signed provenance. -5. Creates the GitHub release. - -Canary builds ship automatically on every push to `main`. +`@vllnt/ui-core` and `@vllnt/ui-native` are experimental. `.github/workflows/native-canary.yml` publishes them as a synchronized pair only on the `canary` tag. It has no manual dispatch, stable tag, Git tag, or GitHub Release path. Enabling a stable native release requires a separate reviewed workflow change. See [docs/RELEASING.md](docs/RELEASING.md). ## Reporting bugs / requesting features diff --git a/README.md b/README.md index 7682f3ac..34ba11b7 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ - **shadcn-compatible registry** — install individual components with `shadcn add` - **TypeScript strict** — fully typed with exported prop interfaces - **Tested** — unit tests (Vitest) + visual regression (Playwright CT) + Storybook +- **React Native pilot** — five experimental components in a separate canary-only renderer with shared tokens and contracts ## Install @@ -45,6 +46,28 @@ Or by `@vllnt-ui` namespace once it's in the [shadcn registry index](https://ui. pnpm dlx shadcn@latest add @vllnt-ui/button ``` +## React Native pilot + +The experimental native renderer is separate so React DOM and Radix dependencies never enter Metro. Install the explicit canary channel: + +```bash +pnpm add @vllnt/ui-native@canary +``` + +```tsx +import { Button, ThemeProvider } from "@vllnt/ui-native"; + +export function NativeExample() { + return ( + + + + ); +} +``` + +The pilot includes Button, Text, Heading, Badge, and Card. See the [React Native guide](https://ui.vllnt.com/docs/native). `@vllnt/ui` remains the stable web renderer with its existing API and release path. + ## Quick Start ```tsx @@ -97,6 +120,8 @@ pnpm dev | `pnpm lint` | Lint all packages | | `pnpm test:once` | Run tests (single run) | | `pnpm check:circular` | Detect circular imports | +| `pnpm ci:native` | Verify core/native packages and Expo bundles | +| `pnpm tokens:check` | Check generated web/native token drift | ## Theming @@ -104,8 +129,8 @@ Override CSS variables after importing styles: ```css :root { - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; + --primary: 0.45 0.16 255; + --primary-foreground: 0.98 0 0; } ``` diff --git a/ROADMAP.md b/ROADMAP.md index 02770323..aa3a8e2f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,10 +2,10 @@ > **Goal:** the design-system foundation for building UI fast — the @vllnt/ui component registry (web + native), the `@vllnt/ui-cli` DX gate, and `@vllnt/front-studio` + `@vllnt/ui-toolbar` (verify · review · author, API-first) — for humans and agents alike. > **Now:** `component-sidebar` — finish `.5`/`.6`, then ship `@vllnt/ui@0.4.0`. -> **Next:** `agent-ui-cli` (MVP) · `ai-elements-parity`. -> **Horizon (gated):** `native-parity` (needs an RN consumer) · the front-studio line (`studio` → `studio-hub` — after the CLI ships + a real need). +> **Next:** `native-parity` pilot validation · `agent-ui-cli` (MVP) · `ai-elements-parity`. +> **Horizon (gated):** the front-studio line (`studio` → `studio-hub` — after the CLI ships + a real need). > **Visibility track:** SEO/GEO phases — `search-consolidation` → `ai-toolchain-registration` → `visibility-measurement` → `seo-content-engine` → `backlink-authority`. Diagnosis: infra is DONE (`agent-surface`) but GSC shows indexed-yet-buried (141 pages, 2 clicks/90d, 0 AI-query visibility) + a dead `.com` twin outranking the live `.ai`. Full plan: [strategy dossier](https://claude.ai/code/artifact/6e2359db-a626-4226-aa54-a9e53ecfd766). -> **Last updated:** 2026-07-02 +> **Last updated:** 2026-09-03 > **Channels:** `@latest` = `0.3.0` · `@canary` = `0.4.0-canary.` (auto-publishes on every merge to main). Tracking: [milestone 0.3.0](https://github.com/vllnt/ui/milestone/1) Convention: phases are kebab-case outcome slugs, ordered DONE → ACTIVE → PLANNED. Tasks carry stable `.` IDs; functional tasks pair with a `Validate`/`E2E` task. History is never deleted. Shipped 0.3.0 detail lives in `CHANGELOG.md` and the 197 closed issues; phases below summarize it. @@ -66,28 +66,24 @@ Single-pane drill-down (chosen over accordion-single-open and a two-pane family - [ ] component-sidebar.6 Directional slide transition with `prefers-reduced-motion` instant fallback; persist last-family + scroll (localStorage) - [~] component-sidebar.7 Validate component-sidebar.1–6: Playwright E2E (desktop + mobile + keyboard) — auto-drill, back, breadcrumb sync, global filter, ⌘K, persistence (E2E); core drill-down (`.1`/`.2`/`.4`) covered by `sidebar-drilldown.spec.ts` — pending `.5`/`.6` -## native-parity [PLANNED] +## native-parity [ACTIVE — experimental package pilot] -**Goal:** Make all 309 @vllnt/ui components iso web↔native — install once from the single `ui.vllnt.com` registry, one import, platform-correct render — with no second registry. -**Exit criteria:** Every component ships a `.native.tsx` twin + a shared `.variants.ts`; one `npx shadcn add @vllnt-ui/` from `ui.vllnt.com/r/.json` installs both files; each renders correctly on a Next.js web app AND an Expo device; each is stamped `parity: full|api-only` in `meta.json` with the badge shown on the site. -**Verify:** a consumer dev runs `npx shadcn add @vllnt-ui/button` once → `import { Button }` renders on web (Radix/DOM) and on an Expo device (rn-primitives) with an identical variant API across all 12 families; the overlay family is documented `api-only` where Portal/keyboard can't map. Personas: consumer dev (web + Expo device, keyboard), maintainer (adds a native twin + variants contract), agent (reads `parity` from the registry JSON). +**Goal:** Add a platform-correct React Native renderer without changing the stable `@vllnt/ui` web contract. Shared tokens and semantic option contracts live in framework-free `@vllnt/ui-core`; implementations remain separate in `@vllnt/ui` and `@vllnt/ui-native`. +**Exit criteria:** the canonical token source generates unchanged web CSS and native-safe values; an Expo catalog bundles the native pilot on Android and iOS; registry JSON, docs, search, and MCP expose renderer availability; native packages can publish synchronized canaries but cannot publish `latest`. +**Verify:** existing web gates and exports stay green; `pnpm ci:native` passes contract tests, package boundaries, Expo Doctor, and both Metro exports; `/components?platform=native` and `search_components({ platform: "native" })` return the same pilot set. -**Gated by:** a confirmed Expo/RN consumer (`native-parity.1`) — **Horizon** until resolved (flagged since 2026-06; still open). +The earlier co-located `.native.tsx` proposal is superseded by the package boundary tracked in #479. Separate renderers prevent DOM/Radix dependencies from entering Metro and let native APIs use `onPress`, `style`, and native accessibility semantics. The shared layer contains data and portable option names, not renderer props. Foundational native components use React Native primitives and `StyleSheet`; NativeWind and `@rn-primitives` remain possible adapters for later complex families when a demonstrated need justifies their consumer configuration and runtime cost. -One registry, not two — platform is resolved by the bundler (Metro picks `.native.tsx`, web picks `.tsx`), so `ui.vllnt.com` stays the single source. Stack maps 1:1: Radix → @rn-primitives, Tailwind `className` → NativeWind, CVA + `cn()` unchanged, lucide-react → lucide-react-native. Only the render body forks; `.variants.ts` (CVA + a platform-neutral prop contract) is shared. Iso *API* is always achievable; iso *visual result* is a per-component property (the `parity` badge) — overlays / hover / keyboard degrade to api-only. Proven prior art: **react-native-reusables** (@rn-primitives + NativeWind, 8.4k★, active 2026) already publishes a shadcn-format `registry.json` for RN — one schema spans web+native, validating the single-registry bet. Open gate: a confirmed Expo consumer (`.1`); OKLCH-on-native is resolved — NativeWind v4 (stable) has no on-device `oklch()`, so an HSL fallback is required (`.2`). - -- [ ] native-parity.1 Decide: confirm an Expo/RN consumer app + the native stack (NativeWind + @rn-primitives + lucide-react-native) -- [ ] native-parity.2 Emit an HSL fallback channel for the native theme (RESEARCHED 2026-07): NativeWind v4 (stable) has no on-device `oklch()` — RNR themes in HSL; native OKLCH is gated on NativeWind v5 (preview). Spike confirms the down-convert on a device; tweakcn already exports OKLCH+HSL from one source -- [ ] native-parity.3 Decide: one registry, multi-file items (`.tsx` + `.native.tsx`, bundler-resolved) — single `ui.vllnt.com`, no second namespace -- [ ] native-parity.4 Token codegen: emit the RN/NativeWind theme from `tokens.json` alongside the web CSS vars (single source) -- [ ] native-parity.5 Establish the iso pattern: extract `.variants.ts` (CVA + platform-neutral prop contract) + ship a 5-component reference set (button, input, card, badge, dialog) -- [ ] native-parity.6 Extend `apps/registry/scripts/inline-component-source.ts` to emit `.native.tsx` + the shared variants per registry item; stamp `parity: full|api-only` in `meta.json`; surface the badge on `ui.vllnt.com` -- [ ] native-parity.7 Port core + form + utility families (115) to native twins -- [ ] native-parity.8 Port data + data-display + content families (109) to native twins -- [ ] native-parity.9 Port navigation + learning + educational + billing + ai families (70) to native twins -- [ ] native-parity.10 Port overlay family (15) — api-only parity where Portal/keyboard can't map; document the degradation -- [ ] native-parity.11 Validate native-parity.5–10: one `shadcn add @vllnt-ui/` from `ui.vllnt.com` → identical import renders on Next web + Expo device across all 12 families; parity badges accurate (E2E) -- [ ] native-parity.12 Validate native-parity.4: an off-token native color fails; `tokens.json` stays the sole source across web CSS + RN theme (E2E) +- [x] native-parity.1 Establish `@vllnt/ui-core` and separate `@vllnt/ui-native` package boundaries while keeping `@vllnt/ui` dependency-free from canary packages +- [x] native-parity.2 Generate web CSS and native sRGB/point tokens from `packages/design/tokens.json`; fail CI on drift +- [x] native-parity.3 Define portable Button, Text, Heading, Badge, and Card contracts and verify web compatibility at compile time +- [x] native-parity.4 Ship the five-component React Native pilot plus light/dark/system theme support and an Expo catalog +- [x] native-parity.5 Add explicit `platforms` and native status/parity metadata across registry JSON, docs, search, JSON-LD, llms surfaces, and MCP +- [x] native-parity.6 Add native quality gates and a separate synchronized canary-only workflow with no stable publish path +- [~] native-parity.7 Validate the pilot on CI and a physical Expo device; keep native experimental until both pass +- [ ] native-parity.8 Expand foundational form and utility components based on real consumer demand +- [ ] native-parity.9 Add complex primitive adapters only where native behavior and accessibility tests require them +- [ ] native-parity.10 Define stable-version policy and migration notes in a separately reviewed release change ## typography-primitives [DONE 2026-07] diff --git a/apps/native-catalog/App.test.tsx b/apps/native-catalog/App.test.tsx new file mode 100644 index 00000000..4ff16547 --- /dev/null +++ b/apps/native-catalog/App.test.tsx @@ -0,0 +1,17 @@ +import { fireEvent, render, screen } from "@testing-library/react-native"; + +import App from "./App"; + +describe("native catalog", () => { + it("renders the pilot and proves interaction", () => { + render(); + + expect(screen.getByText("VLLNT UI Native")).toBeOnTheScreen(); + expect(screen.getByText("Separate native renderer")).toBeOnTheScreen(); + expect(screen.getByText("Button presses: 0")).toBeOnTheScreen(); + + fireEvent.press(screen.getByRole("button", { name: "Try interaction" })); + + expect(screen.getByText("Button presses: 1")).toBeOnTheScreen(); + }); +}); diff --git a/apps/native-catalog/App.tsx b/apps/native-catalog/App.tsx new file mode 100644 index 00000000..510a6afe --- /dev/null +++ b/apps/native-catalog/App.tsx @@ -0,0 +1,79 @@ +import { useState } from "react"; + +import { + Heading, + Text, + ThemeProvider, + type ThemeSelection, + useTheme, +} from "@vllnt/ui-native"; +import { StatusBar } from "expo-status-bar"; +import { ScrollView, View } from "react-native"; + +import { + BadgeSection, + ButtonSection, + CardSection, + ThemeSection, + TypeSection, +} from "./catalog-sections"; + +function CatalogContent({ + onThemeChange, + themeSelection, +}: { + readonly onThemeChange: (selection: ThemeSelection) => void; + readonly themeSelection: ThemeSelection; +}) { + const theme = useTheme(); + const [presses, setPresses] = useState(0); + const incrementPresses = () => { + setPresses((value) => value + 1); + }; + + return ( + + + + + + VLLNT UI Native + + + Shared tokens and contracts. React Native renderer. + + + + + + + + + + ); +} +CatalogContent.displayName = "CatalogContent"; + +export default function App() { + const [themeSelection, setThemeSelection] = + useState("system"); + + return ( + + + + ); +} diff --git a/apps/native-catalog/README.md b/apps/native-catalog/README.md new file mode 100644 index 00000000..21460c42 --- /dev/null +++ b/apps/native-catalog/README.md @@ -0,0 +1,10 @@ +# VLLNT UI native catalog + +Private Expo consumer for the experimental `@vllnt/ui-native` package. It exercises every pilot component, semantic variant, light/dark/system theme selection, and interaction in one scrollable screen. + +```bash +pnpm -F @vllnt/ui-native-catalog dev +pnpm -F @vllnt/ui-native-catalog build +``` + +The build exports Android and iOS JavaScript bundles in CI. This checks Metro workspace resolution without publishing or requiring a simulator. Real-device validation remains required before a stable native release. diff --git a/apps/native-catalog/app.json b/apps/native-catalog/app.json new file mode 100644 index 00000000..50c67bb1 --- /dev/null +++ b/apps/native-catalog/app.json @@ -0,0 +1,16 @@ +{ + "expo": { + "name": "VLLNT UI Native Catalog", + "slug": "vllnt-ui-native-catalog", + "version": "0.1.0", + "orientation": "portrait", + "userInterfaceStyle": "automatic", + "android": { + "package": "com.vllnt.uinativecatalog" + }, + "ios": { + "bundleIdentifier": "com.vllnt.uinativecatalog", + "supportsTablet": true + } + } +} diff --git a/apps/native-catalog/babel.config.cjs b/apps/native-catalog/babel.config.cjs new file mode 100644 index 00000000..54751c2d --- /dev/null +++ b/apps/native-catalog/babel.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + presets: ["babel-preset-expo"], +}; diff --git a/apps/native-catalog/catalog-sections.tsx b/apps/native-catalog/catalog-sections.tsx new file mode 100644 index 00000000..a8942acc --- /dev/null +++ b/apps/native-catalog/catalog-sections.tsx @@ -0,0 +1,183 @@ +import { + Badge, + Button, + type ButtonSize, + type ButtonVariant, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, + Heading, + Text, + type ThemeSelection, + useTheme, +} from "@vllnt/ui-native"; +import type { ReactNode } from "react"; +import { View } from "react-native"; + +const buttonVariants: readonly ButtonVariant[] = [ + "default", + "secondary", + "outline", + "ghost", + "link", + "destructive", +]; +const buttonSizes: readonly ButtonSize[] = ["sm", "default", "lg", "icon"]; +const themeSelections: readonly ThemeSelection[] = ["system", "light", "dark"]; + +function Row({ children }: { readonly children: ReactNode }) { + const theme = useTheme(); + return ( + + {children} + + ); +} +Row.displayName = "Row"; + +function Section({ + children, + title, +}: { + readonly children: ReactNode; + readonly title: string; +}) { + const theme = useTheme(); + return ( + + + {title} + + {children} + + ); +} +Section.displayName = "Section"; + +export function ThemeSection({ + onChange, + selection, +}: { + readonly onChange: (selection: ThemeSelection) => void; + readonly selection: ThemeSelection; +}) { + return ( +
+ + {themeSelections.map((value) => ( + + ))} + +
+ ); +} + +export function ButtonSection({ + onPress, + presses, +}: { + readonly onPress: () => void; + readonly presses: number; +}) { + return ( +
+ + {buttonVariants.map((variant) => ( + + ))} + + + {buttonSizes.map((size) => ( + + ))} + + + Button presses: {presses} + +
+ ); +} + +export function TypeSection() { + return ( +
+ + Semantic h3 at h1 size + + Lead body text + Default body text + + Muted small text + + + Semibold caption + +
+ ); +} + +export function BadgeSection() { + return ( +
+ + Default + Secondary + Outline + Destructive + +
+ ); +} + +export function CardSection({ onPress }: { readonly onPress: () => void }) { + const theme = useTheme(); + return ( + + + Experimental + Separate native renderer + + The web API remains intact while native uses platform-correct + primitives. + + + + Five pilot components share canonical design tokens. + + No DOM, Radix, Tailwind, or NativeWind runtime is required. + + + + + + + ); +} diff --git a/apps/native-catalog/eslint.config.js b/apps/native-catalog/eslint.config.js new file mode 100644 index 00000000..06517239 --- /dev/null +++ b/apps/native-catalog/eslint.config.js @@ -0,0 +1,28 @@ +import { react } from "@vllnt/eslint-config"; + +export default [ + { + ignores: [ + ".expo/**", + "dist/**", + "node_modules/**", + "eslint.config.js", + "*.config.cjs", + ], + }, + ...react, + { + linterOptions: { + reportUnusedDisableDirectives: "error", + }, + rules: { + "@next/next/no-html-link-for-pages": "off", + }, + }, + { + files: ["**/*.test.{ts,tsx}"], + rules: { + "max-lines-per-function": "off", + }, + }, +]; diff --git a/apps/native-catalog/index.ts b/apps/native-catalog/index.ts new file mode 100644 index 00000000..e5802d26 --- /dev/null +++ b/apps/native-catalog/index.ts @@ -0,0 +1,5 @@ +import { registerRootComponent } from "expo"; + +import App from "./App"; + +registerRootComponent(App); diff --git a/apps/native-catalog/jest.config.cjs b/apps/native-catalog/jest.config.cjs new file mode 100644 index 00000000..343915fc --- /dev/null +++ b/apps/native-catalog/jest.config.cjs @@ -0,0 +1,12 @@ +/** @type {import('jest').Config} */ +module.exports = { + preset: "react-native", + moduleNameMapper: { + "^@vllnt/ui-core$": "/../../packages/ui-core/src/index.ts", + "^@vllnt/ui-native$": "/../../packages/ui-native/src/index.ts", + }, + testMatch: ["/**/*.test.ts", "/**/*.test.tsx"], + transformIgnorePatterns: [ + "node_modules/(?!((?:\\.pnpm/[^/]+/node_modules/)?(?:react-native|@react-native(?:-community)?|@testing-library/react-native|@vllnt/ui-native|expo(?:nent)?|@expo(?:nent)?/.*|expo-status-bar))/)", + ], +}; diff --git a/apps/native-catalog/metro.config.cjs b/apps/native-catalog/metro.config.cjs new file mode 100644 index 00000000..aad375dc --- /dev/null +++ b/apps/native-catalog/metro.config.cjs @@ -0,0 +1,3 @@ +const { getDefaultConfig } = require("expo/metro-config"); + +module.exports = getDefaultConfig(__dirname); diff --git a/apps/native-catalog/package.json b/apps/native-catalog/package.json new file mode 100644 index 00000000..7c266822 --- /dev/null +++ b/apps/native-catalog/package.json @@ -0,0 +1,52 @@ +{ + "name": "@vllnt/ui-native-catalog", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "index.ts", + "expo": { + "install": { + "exclude": ["typescript"] + } + }, + "scripts": { + "android": "expo start --android", + "build": "expo export --platform android --output-dir dist/android && expo export --platform ios --output-dir dist/ios", + "build:renderer": "pnpm --filter '@vllnt/ui-native...' build", + "clean": "rm -rf .expo dist node_modules coverage", + "dev": "expo start", + "doctor": "expo-doctor", + "ios": "expo start --ios", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "preandroid": "pnpm build:renderer", + "prebuild": "pnpm build:renderer", + "predev": "pnpm build:renderer", + "preios": "pnpm build:renderer", + "test:once": "jest --runInBand", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@vllnt/ui-native": "workspace:*", + "expo": "~57.0.19", + "expo-status-bar": "~57.0.1", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-native": "0.86.3", + "react-native-web": "^0.21.2" + }, + "devDependencies": { + "@react-native/babel-preset": "0.86.3", + "@testing-library/react-native": "^13.3.3", + "@types/jest": "^29.5.14", + "@types/react": "19.2.13", + "@vllnt/eslint-config": "^1.0.0", + "@vllnt/typescript": "^1.0.0", + "babel-jest": "^29.7.0", + "eslint": "^9.39.1", + "expo-doctor": "^1.20.4", + "jest": "^29.7.0", + "react-test-renderer": "19.2.3", + "typescript": "^5.9.3" + } +} diff --git a/apps/native-catalog/tsconfig.json b/apps/native-catalog/tsconfig.json new file mode 100644 index 00000000..262e8709 --- /dev/null +++ b/apps/native-catalog/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "types": ["jest", "react", "react-native"] + }, + "include": ["**/*.ts", "**/*.tsx"] +} diff --git a/apps/registry/app/[locale]/components/[slug]/page.tsx b/apps/registry/app/[locale]/components/[slug]/page.tsx index e3e0001d..375303c7 100644 --- a/apps/registry/app/[locale]/components/[slug]/page.tsx +++ b/apps/registry/app/[locale]/components/[slug]/page.tsx @@ -16,6 +16,7 @@ import { getTranslations, setRequestLocale } from "next-intl/server"; import { ComponentCard } from "@/components/component-card"; import { buildComponentMdxKit } from "@/components/component-mdx"; +import { PlatformBadges } from "@/components/platform-badges"; import { PreviewPlaygroundTabs } from "@/components/playground"; import { QuickAdd } from "@/components/quick-add"; import { ShareEmbedBar } from "@/components/share-embed-bar"; @@ -63,6 +64,7 @@ const metadata_map = componentMetadata as Record< defaultStoryId: string; description: string; name: string; + platforms: ("native" | "web")[]; stories: { id: string; name: string }[]; title: string; } @@ -180,6 +182,7 @@ export default async function ComponentPage(props: Props) { ""; const playgroundExample = getPlaygroundExample(component); const registryPackageVersion = getRegistryPackageVersion(registry.version); + const supportsNative = component.platforms.includes("native"); // Read component source for code display let componentCode = ""; @@ -264,10 +267,13 @@ export default async function ComponentPage(props: Props) { const sections = [ ...(meta?.defaultStoryId ? [{ id: "preview", title: t("preview") }] : []), { id: "installation", title: t("installation") }, - ...(componentCode ? [{ id: "code", title: t("code") }] : []), ...(meta?.defaultStoryId ? [{ id: "storybook", title: t("storybook") }] : []), + ...(componentCode ? [{ id: "code", title: t("code") }] : []), + ...(supportsNative + ? [{ id: "native-installation", title: t("nativeInstallation") }] + : []), ...(component.dependencies && component.dependencies.length > 0 ? [{ id: "dependencies", title: t("dependencies") }] : []), @@ -300,6 +306,7 @@ export default async function ComponentPage(props: Props) { keywords: componentMdx?.frontmatter.keywords, locale, name: component.name, + platforms: component.platforms, title: articleTitle, }), techArticleLd({ @@ -363,9 +370,13 @@ export default async function ComponentPage(props: Props) { ]} />

{articleTitle}

-

+

{articleDescription}

+
)} + {supportsNative ? ( +
+

+ {t("nativeInstallation")} +

+

+ {t("nativeInstallDescription")} +

+ + + {t("nativeReadGuide")} + +
+ ) : null} + {/* Dependencies */} {component.dependencies && component.dependencies.length > 0 ? (
diff --git a/apps/registry/app/[locale]/components/page.tsx b/apps/registry/app/[locale]/components/page.tsx index 760f17c7..259b0407 100644 --- a/apps/registry/app/[locale]/components/page.tsx +++ b/apps/registry/app/[locale]/components/page.tsx @@ -1,10 +1,9 @@ import { Sidebar } from "@vllnt/ui"; import type { Metadata } from "next"; -import Link from "next/link"; import { getTranslations, setRequestLocale } from "next-intl/server"; import { ComponentCard } from "@/components/component-card"; -import type { Locale } from "@/i18n/routing"; +import { Link, type Locale } from "@/i18n/routing"; import { getPageContent } from "@/lib/content"; import { breadcrumbTrailLd, @@ -12,9 +11,13 @@ import { jsonLdScriptAttributes, } from "@/lib/jsonld"; import { generateOGMetadata, generateTwitterMetadata } from "@/lib/og"; -import { canonical, languageAlternates, localizePathname } from "@/lib/seo"; import { - components, + type ComponentPlatform, + componentPlatformSchema, + registry, +} from "@/lib/registry"; +import { canonical, languageAlternates } from "@/lib/seo"; +import { familyPath, getSidebarSections, groupedComponents, @@ -22,6 +25,7 @@ import { type Props = { readonly params: Promise<{ locale: Locale }>; + readonly searchParams: Promise<{ platform?: string }>; }; export async function generateMetadata({ params }: Props): Promise { @@ -52,11 +56,49 @@ export async function generateMetadata({ params }: Props): Promise { }; } -export default async function ComponentsPage({ params }: Props) { - const { locale } = await params; +export default async function ComponentsPage({ params, searchParams }: Props) { + const [{ locale }, query] = await Promise.all([params, searchParams]); setRequestLocale(locale); const t = await getTranslations("pages.components"); const common = await getTranslations("common"); + const parsedPlatform = componentPlatformSchema.safeParse(query.platform); + const selectedPlatform = parsedPlatform.success + ? parsedPlatform.data + : undefined; + const platformsByName = new Map( + registry.items.map((item) => [item.name, item.platforms]), + ); + const visibleGroups = groupedComponents + .map((group) => ({ + ...group, + items: selectedPlatform + ? group.items.filter((item) => + platformsByName.get(item.name)?.includes(selectedPlatform), + ) + : group.items, + })) + .filter((group) => group.items.length > 0); + const visibleCount = visibleGroups.reduce( + (count, group) => count + group.items.length, + 0, + ); + const filters: readonly { + href: string; + label: string; + platform?: ComponentPlatform; + }[] = [ + { href: "/components", label: t("platformAll") }, + { + href: "/components?platform=web", + label: t("platformWeb"), + platform: "web", + }, + { + href: "/components?platform=native", + label: t("platformNative"), + platform: "native", + }, + ]; return ( <> @@ -66,14 +108,14 @@ export default async function ComponentsPage({ params }: Props) { { name: "Components", path: "/components" }, ]), collectionPageLd({ - description: `Browse all ${components.length} accessible React components in VLLNT UI — installable with the shadcn CLI.`, - items: groupedComponents.flatMap((group) => + description: t("description", { count: visibleCount }), + items: visibleGroups.flatMap((group) => group.items.map((item) => ({ name: item.title, url: canonical(`/components/${item.name}`, locale), })), ), - title: "Components", + title: t("title"), url: canonical("/components", locale), }), ])} @@ -84,16 +126,34 @@ export default async function ComponentsPage({ params }: Props) {

{t("title")}

- {t("description", { count: components.length })} + {t("description", { count: visibleCount })}

+
- {groupedComponents.map((group) => ( + {visibleGroups.map((group) => (

{group.label} @@ -110,6 +170,12 @@ export default async function ComponentsPage({ params }: Props) {

))} + {visibleGroups.length === 0 ? ( +

+ {t("noPlatformResults")} +

+ ) : null} +

{t("ctaTitle")}

@@ -117,7 +183,7 @@ export default async function ComponentsPage({ params }: Props) {

{common("requestComponent")} diff --git a/apps/registry/app/llms-full.txt/route.ts b/apps/registry/app/llms-full.txt/route.ts index 5e3cbabc..2271a340 100644 --- a/apps/registry/app/llms-full.txt/route.ts +++ b/apps/registry/app/llms-full.txt/route.ts @@ -54,9 +54,9 @@ async function readDocumentPage(slug: string): Promise { function buildSummary(items: readonly RegistryComponent[]): string { return ( - "One-fetch, complete agent context for the VLLNT UI registry. " + - `${items.length} components, install via shadcn CLI against /r/.json. ` + - `Site: ${SITE_URL}` + "One-fetch, complete agent context for the platform-aware VLLNT UI registry. " + + `${items.length} component descriptors for web and React Native. ` + + `Inspect platforms before installation. Site: ${SITE_URL}` ); } @@ -67,6 +67,12 @@ const INSTALL_DETAILS = [ `pnpm dlx shadcn@latest add ${SITE_URL}/r/.json`, `# Or with npm: npx shadcn@latest add ${SITE_URL}/r/.json`, "```", + "", + "Experimental React Native renderer:", + "", + "```bash", + "pnpm add @vllnt/ui-native@canary", + "```", ].join("\n"); async function buildGuidePages(): Promise { @@ -130,6 +136,12 @@ function buildComponentPages( content: [ `- Slug: \`${item.name}\``, `- Category: \`${item.category ?? ""}\``, + `- Platforms: ${item.platforms.join(", ")}`, + ...(item.native + ? [ + `- Native package: \`${item.native.package}@${item.native.channel}\` (${item.native.status}, ${item.native.parity} parity)`, + ] + : []), `- Description: ${item.description ?? ""}`, `- Page: ${SITE_URL}/components/${item.name}`, `- Schema: ${SITE_URL}/r/${item.name}.json`, diff --git a/apps/registry/app/llms.txt/route.ts b/apps/registry/app/llms.txt/route.ts index e9581966..8b5a41d5 100644 --- a/apps/registry/app/llms.txt/route.ts +++ b/apps/registry/app/llms.txt/route.ts @@ -47,9 +47,11 @@ const CATEGORY_LABEL = new Map([ ["utility", "Utility"], ]); -const INSTALL_DETAILS = - "Install any component with the shadcn CLI: " + - `\`pnpm dlx shadcn@latest add ${SITE_URL}/r/.json\``; +const INSTALL_DETAILS = [ + "Web: install a component with the shadcn CLI: " + + `\`pnpm dlx shadcn@latest add ${SITE_URL}/r/.json\`.`, + "Native (experimental): `pnpm add @vllnt/ui-native@canary`; check an item's `platforms` and `native` metadata first.", +].join(" "); const DOCS_SECTION: LlmsSection = { links: [ @@ -161,9 +163,9 @@ function getSortedCategories( function buildSummary(items: readonly RegistryComponent[]): string { return ( - "Agent-first React component registry. " + - `${items.length} accessible components built on Radix UI, Tailwind CSS, and CVA. ` + - "Install via the shadcn CLI against any /r/.json endpoint." + "Agent-first, platform-aware component registry. " + + `${items.length} accessible descriptors for the stable web renderer and experimental React Native renderer. ` + + "Inspect each item's platforms before choosing an install path." ); } @@ -178,7 +180,7 @@ function buildComponentSections( const links: LlmsLink[] = [...bucket] .sort((a, b) => a.name.localeCompare(b.name)) .map((item) => ({ - notes: item.description, + notes: `${item.description ?? ""} Platforms: ${item.platforms.join(", ")}.`, title: item.title, url: `${SITE_URL}/components/${item.name}`, })); diff --git a/apps/registry/app/mcp/route.test.ts b/apps/registry/app/mcp/route.test.ts new file mode 100644 index 00000000..a2bdbed8 --- /dev/null +++ b/apps/registry/app/mcp/route.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from "vitest"; + +import { getComponent, searchComponents, TOOLS } from "./route"; + +describe("platform-aware MCP tools", () => { + it("advertises the renderer filter", () => { + expect(TOOLS[0].inputSchema.properties.platform).toMatchObject({ + enum: ["web", "native"], + type: "string", + }); + }); + + it("filters native-capable components", () => { + const result = searchComponents({ platform: "native", query: "" }); + + expect(result.total).toBe(5); + expect(result.items.map((item) => item.name)).toEqual([ + "badge", + "button", + "card", + "heading", + "text", + ]); + expect( + result.items.every((item) => item.platforms.includes("native")), + ).toBe(true); + }); + + it("returns native metadata from get_component", () => { + expect(getComponent({ name: "button" })).toMatchObject({ + native: { + channel: "canary", + package: "@vllnt/ui-native", + status: "experimental", + }, + platforms: ["web", "native"], + }); + }); +}); diff --git a/apps/registry/app/mcp/route.ts b/apps/registry/app/mcp/route.ts index 71a952b2..2e0bc7bf 100644 --- a/apps/registry/app/mcp/route.ts +++ b/apps/registry/app/mcp/route.ts @@ -9,7 +9,7 @@ * - tools/call → invoke a tool by name * * Tools (UI registry scope — see #246 scope decision): - * - search_components({ query, category?, limit? }) + * - search_components({ query, category?, platform?, limit? }) * - get_component({ name }) * - list_categories() * @@ -25,6 +25,7 @@ * source of truth as /r/registry.json. No DB, no auth, no writes. */ +import { isComponentPlatform } from "@vllnt/ui-core"; import { NextResponse } from "next/server"; import { registry as REGISTRY, type RegistryComponent } from "@/lib/registry"; @@ -71,10 +72,10 @@ const error_ = ( jsonrpc: "2.0", }); -const TOOLS = [ +export const TOOLS = [ { description: - "Search VLLNT UI components by name / title / description. Filter by category.", + "Search VLLNT UI components by name / title / description. Filter by category or platform.", inputSchema: { properties: { category: { @@ -86,9 +87,14 @@ const TOOLS = [ description: "Maximum results (default 25, capped at 100).", type: "number", }, + platform: { + description: "Optional renderer filter.", + enum: ["web", "native"], + type: "string", + }, query: { description: - "Free-text query matched against name, title, description (case-insensitive).", + "Free-text query matched against name, title, description, category, and platform (case-insensitive).", type: "string", }, }, @@ -121,7 +127,7 @@ const TOOLS = [ const isObject = (value: unknown): value is Record => typeof value === "object" && value !== null; -function searchComponents(arguments_: Record): { +export function searchComponents(arguments_: Record): { items: RegistryComponent[]; total: number; } { @@ -131,6 +137,10 @@ function searchComponents(arguments_: Record): { typeof arguments_.category === "string" ? arguments_.category.toLowerCase() : null; + const platform = + typeof arguments_.platform === "string" + ? arguments_.platform.toLowerCase() + : null; const requested = typeof arguments_.limit === "number" && arguments_.limit > 0 ? arguments_.limit @@ -141,12 +151,19 @@ function searchComponents(arguments_: Record): { if (category && (item.category ?? "").toLowerCase() !== category) { return false; } + if ( + platform && + (!isComponentPlatform(platform) || !item.platforms.includes(platform)) + ) { + return false; + } if (!query) return true; const haystack = [ item.name, item.title, item.description ?? "", item.category ?? "", + ...item.platforms, ] .join(" ") .toLowerCase(); @@ -156,7 +173,7 @@ function searchComponents(arguments_: Record): { return { items: items.slice(0, limit), total: items.length }; } -function getComponent( +export function getComponent( arguments_: Record, ): null | RegistryComponent { const name = typeof arguments_.name === "string" ? arguments_.name : null; @@ -194,7 +211,7 @@ function callTool( "", ...items.map( (item) => - `- ${item.name} (${item.category ?? "uncategorized"}): ${item.title}${ + `- ${item.name} (${item.category ?? "uncategorized"}; ${item.platforms.join(", ")}): ${item.title}${ item.description ? ` — ${item.description}` : "" }`, ), @@ -249,7 +266,7 @@ function dispatch(request: JsonRpcRequest): JsonRpcError | JsonRpcSuccess { tools: { listChanged: false }, }, instructions: - `VLLNT UI registry MCP server. Use search_components / get_component / list_categories to discover and read ${REGISTRY.items.length} React components. Source of truth: ` + + `VLLNT UI registry MCP server. Use search_components / get_component / list_categories to discover and read ${REGISTRY.items.length} web and React Native component descriptors. Source of truth: ` + SITE_URL + "/r/registry.json", protocolVersion: PROTOCOL_VERSION, @@ -303,7 +320,7 @@ function dispatch(request: JsonRpcRequest): JsonRpcError | JsonRpcSuccess { const SERVER_INFO = { capabilities: { tools: { listChanged: false } }, description: - "MCP server for the VLLNT UI component registry. Tools: search_components, get_component, list_categories.", + "MCP server for the platform-aware VLLNT UI component registry. Tools: search_components, get_component, list_categories. Native entries are experimental.", endpoint: `${SITE_URL}/mcp`, name: "vllnt-ui", protocol: PROTOCOL_VERSION, diff --git a/apps/registry/app/r/registry.json/route.ts b/apps/registry/app/r/registry.json/route.ts index 5d673cda..98612f1d 100644 --- a/apps/registry/app/r/registry.json/route.ts +++ b/apps/registry/app/r/registry.json/route.ts @@ -1,8 +1,8 @@ import { NextResponse } from "next/server"; -import registryData from "@/registry.json"; +import { registry } from "@/lib/registry"; // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/require-await export async function GET() { - return NextResponse.json(registryData); + return NextResponse.json(registry); } diff --git a/apps/registry/components/component-card/component-card.tsx b/apps/registry/components/component-card/component-card.tsx index aa018fb1..bc91ee70 100644 --- a/apps/registry/components/component-card/component-card.tsx +++ b/apps/registry/components/component-card/component-card.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { ComponentThumbnail } from "@/components/component-thumbnail"; +import { PlatformBadges } from "@/components/platform-badges"; import type { Locale } from "@/i18n/routing"; import { getComponentContent } from "@/lib/component-content"; import componentMetadata from "@/lib/component-metadata.json"; @@ -11,6 +12,7 @@ const META = componentMetadata as Record< string, { description?: string; + platforms?: ("native" | "web")[]; stories?: { id: string; name: string }[]; title?: string; } @@ -64,6 +66,10 @@ export async function ComponentCard({ {displayDescription}

) : null} + {storyCount > 0 ? ( {t("stories", { count: storyCount })} diff --git a/apps/registry/components/header/header.tsx b/apps/registry/components/header/header.tsx index bad18b6c..31405b01 100644 --- a/apps/registry/components/header/header.tsx +++ b/apps/registry/components/header/header.tsx @@ -56,7 +56,13 @@ export function Header({ locale }: HeaderProps) { ]; const searchItems = registryData.items.reduce< - { description?: string; href?: string; id: string; title: string }[] + { + description?: string; + href?: string; + id: string; + keywords?: string; + title: string; + }[] >((items, item) => { if (item.type !== "registry:component") return items; @@ -64,6 +70,7 @@ export function Header({ locale }: HeaderProps) { description: item.description, href: localizePathname(`/components/${item.name}`, locale), id: item.name, + keywords: [item.category, ...(item.platforms ?? ["web"])].join(" "), title: item.title, }); diff --git a/apps/registry/components/platform-badges/index.ts b/apps/registry/components/platform-badges/index.ts new file mode 100644 index 00000000..c884718f --- /dev/null +++ b/apps/registry/components/platform-badges/index.ts @@ -0,0 +1 @@ +export { PlatformBadges } from "./platform-badges"; diff --git a/apps/registry/components/platform-badges/platform-badges.tsx b/apps/registry/components/platform-badges/platform-badges.tsx new file mode 100644 index 00000000..5648380d --- /dev/null +++ b/apps/registry/components/platform-badges/platform-badges.tsx @@ -0,0 +1,31 @@ +import { Badge } from "@vllnt/ui"; +import { getTranslations } from "next-intl/server"; + +import type { ComponentPlatform } from "@/lib/registry"; + +type PlatformBadgesProps = { + readonly className?: string; + readonly platforms: readonly ComponentPlatform[]; +}; + +/** Localized renderer availability badges for component cards and details. */ +export async function PlatformBadges({ + className, + platforms, +}: PlatformBadgesProps) { + const t = await getTranslations("common"); + + return ( +
+ {platforms.includes("web") ? ( + {t("platformWeb")} + ) : null} + {platforms.includes("native") ? ( + {t("platformNativeExperimental")} + ) : null} +
+ ); +} diff --git a/apps/registry/content/pages/docs/agents/en.mdx b/apps/registry/content/pages/docs/agents/en.mdx index 0c6b1e73..7f18c083 100644 --- a/apps/registry/content/pages/docs/agents/en.mdx +++ b/apps/registry/content/pages/docs/agents/en.mdx @@ -30,11 +30,11 @@ https://ui.vllnt.com/llms-full.txt ## Registry JSON -Use `/r/registry.json` for discovery and `/r/.json` for exact install descriptors. JSON is the most reliable source for file paths, dependencies, and registry dependencies. +Use `/r/registry.json` for discovery and `/r/.json` for exact install descriptors. JSON is the most reliable source for platforms, file paths, dependencies, and registry dependencies. Native-capable entries identify `@vllnt/ui-native`, its canary channel, experimental status, and parity level. ## MCP -The `/mcp` route is reserved for agent workflows that need a structured protocol endpoint. Treat registry JSON as the canonical install contract and MCP as an integration surface. +The `/mcp` route is reserved for agent workflows that need a structured protocol endpoint. Treat registry JSON as the canonical install contract and MCP as an integration surface. Use `search_components` with `{ "platform": "native" }` to discover the native pilot without scraping pages. ## Safe agent workflow diff --git a/apps/registry/content/pages/docs/agents/fr.mdx b/apps/registry/content/pages/docs/agents/fr.mdx index 245c2ea5..53fc7a38 100644 --- a/apps/registry/content/pages/docs/agents/fr.mdx +++ b/apps/registry/content/pages/docs/agents/fr.mdx @@ -30,11 +30,11 @@ https://ui.vllnt.com/llms-full.txt ## JSON du registre -Utilisez `/r/registry.json` pour la decouverte et `/r/[name].json` pour les descripteurs d'installation exacts. Le JSON est la source la plus fiable pour les chemins de fichiers, les dependances et les dependances de registre. +Utilisez `/r/registry.json` pour la decouverte et `/r/[name].json` pour les descripteurs d'installation exacts. Le JSON est la source la plus fiable pour les plateformes, les chemins de fichiers, les dependances et les dependances de registre. Les entrees compatibles avec le natif identifient `@vllnt/ui-native`, son canal canary, son statut experimental et son niveau de parite. ## MCP -La route `/mcp` est reservee aux flux de travail d'agents qui necessitent un point de terminaison de protocole structure. Traitez le JSON du registre comme le contrat d'installation canonique et le MCP comme une surface d'integration. +La route `/mcp` est reservee aux flux de travail d'agents qui necessitent un point de terminaison de protocole structure. Traitez le JSON du registre comme le contrat d'installation canonique et le MCP comme une surface d'integration. Utilisez `search_components` avec `{ "platform": "native" }` pour decouvrir le pilote natif sans extraire les pages. ## Flux de travail d'agent securise diff --git a/apps/registry/content/pages/docs/native/en.mdx b/apps/registry/content/pages/docs/native/en.mdx new file mode 100644 index 00000000..cf86c72c --- /dev/null +++ b/apps/registry/content/pages/docs/native/en.mdx @@ -0,0 +1,84 @@ +--- +title: React Native +description: Build Expo apps with the experimental React Native renderer and shared VLLNT UI tokens. +type: docs +og: + title: React Native + description: Experimental VLLNT UI components for Expo and React Native + type: docs +--- + +# React Native + +`@vllnt/ui-native` is a separate React Native renderer built on the same authored design tokens and portable option contracts as `@vllnt/ui`. The stable web package remains unchanged: web uses DOM, Radix UI, Tailwind CSS, and CVA; native uses React Native primitives and `StyleSheet`. + +The native package is experimental and publishes only to the `canary` tag. Do not depend on an unqualified or `latest` release yet. + +## Install + +```bash +pnpm add @vllnt/ui-native@canary +``` + +React 19 and React Native 0.81 or newer are peer dependencies. The repository catalog targets Expo SDK 57 and validates Android and iOS Metro bundles. + +## Set up a theme + +```tsx +import { ThemeProvider } from "@vllnt/ui-native" + +export function Root() { + return ( + + + + ) +} +``` + +The provider follows the device theme by default. Choose `light` or `dark` for a fixed mode, or pass semantic overrides through `override`. Native sRGB values are generated from the canonical OKLCH source in `packages/design/tokens.json`, with deterministic contrast and near-black adjustments for native accessibility. + +## Pilot components + +The first catalog includes: + +- `Button` with the web renderer's variant and size names and a plain text or numeric label. +- `Text` with shared size, tone, and weight names. +- `Heading` with independent semantic `level` and visual `size`. +- `Badge` with shared semantic variants. +- `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, and `CardFooter`. + +```tsx +import { + Button, + Card, + CardContent, + CardHeader, + CardTitle, + Text, +} from "@vllnt/ui-native" + +export function Example() { + return ( + + + Account + + + Native semantic tokens. + + + + ) +} +``` + +Use React Native props such as `onPress`, `style`, and `accessibilityLabel`. Web-only props such as `className`, `onClick`, and `asChild` are intentionally not part of the native API. + +## Discover support + +Every registry item now includes `platforms`. Native-capable pilot entries also include a `native` object with package, channel, status, and parity metadata. Filter the components page with `/components?platform=native`, or call MCP `search_components` with `{ "platform": "native" }`. + +## Current boundary + +The pilot uses React Native primitives directly. It does not add NativeWind or `@rn-primitives`, avoiding mandatory Babel and consumer configuration for foundational components. Complex overlays may introduce adapter dependencies later, after real-device behavior and accessibility are validated. diff --git a/apps/registry/content/pages/docs/native/fr.mdx b/apps/registry/content/pages/docs/native/fr.mdx new file mode 100644 index 00000000..3ef4ade2 --- /dev/null +++ b/apps/registry/content/pages/docs/native/fr.mdx @@ -0,0 +1,84 @@ +--- +title: React Native +description: Construisez des applications Expo avec le renderer React Native experimental et les jetons partages de VLLNT UI. +type: docs +og: + title: React Native + description: Composants VLLNT UI experimentaux pour Expo et React Native + type: docs +--- + +# React Native + +`@vllnt/ui-native` est un renderer React Native separe, construit avec les memes jetons de design et contrats d'options portables que `@vllnt/ui`. Le paquet web stable reste inchange : le web utilise le DOM, Radix UI, Tailwind CSS et CVA ; le natif utilise les primitives React Native et `StyleSheet`. + +Le paquet natif est experimental et publie uniquement sur le tag `canary`. Ne dependez pas encore d'une version sans tag ou `latest`. + +## Installer + +```bash +pnpm add @vllnt/ui-native@canary +``` + +React 19 et React Native 0.81 ou plus recent sont des peer dependencies. Le catalogue du depot cible Expo SDK 57 et valide les bundles Metro Android et iOS. + +## Configurer un theme + +```tsx +import { ThemeProvider } from "@vllnt/ui-native" + +export function Root() { + return ( + + + + ) +} +``` + +Le provider suit le theme de l'appareil par defaut. Choisissez `light` ou `dark` pour un mode fixe, ou transmettez des remplacements semantiques via `override`. Les valeurs sRGB natives sont generees depuis la source OKLCH canonique dans `packages/design/tokens.json`, avec des ajustements deterministes de contraste et de fond presque noir pour l'accessibilite native. + +## Composants pilotes + +Le premier catalogue inclut : + +- `Button` avec les noms de variante et de taille du renderer web et un libelle texte ou numerique. +- `Text` avec les noms partages de taille, ton et poids. +- `Heading` avec un `level` semantique et une `size` visuelle independants. +- `Badge` avec les variantes semantiques partagees. +- `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent` et `CardFooter`. + +```tsx +import { + Button, + Card, + CardContent, + CardHeader, + CardTitle, + Text, +} from "@vllnt/ui-native" + +export function Example() { + return ( + + + Compte + + + Jetons semantiques natifs. + + + + ) +} +``` + +Utilisez les props React Native comme `onPress`, `style` et `accessibilityLabel`. Les props web comme `className`, `onClick` et `asChild` ne font volontairement pas partie de l'API native. + +## Decouvrir la prise en charge + +Chaque element du registre inclut maintenant `platforms`. Les entrees pilotes compatibles avec le natif incluent aussi un objet `native` avec les metadonnees de paquet, canal, statut et parite. Filtrez la page des composants avec `/fr/components?platform=native`, ou appelez MCP `search_components` avec `{ "platform": "native" }`. + +## Limite actuelle + +Le pilote utilise directement les primitives React Native. Il n'ajoute ni NativeWind ni `@rn-primitives`, ce qui evite une configuration Babel obligatoire chez le consommateur pour les composants fondamentaux. Les overlays complexes pourront introduire des dependances d'adaptation plus tard, apres validation du comportement et de l'accessibilite sur appareil reel. diff --git a/apps/registry/content/pages/docs/registry/en.mdx b/apps/registry/content/pages/docs/registry/en.mdx index 6b2aa5a2..e5ea06f2 100644 --- a/apps/registry/content/pages/docs/registry/en.mdx +++ b/apps/registry/content/pages/docs/registry/en.mdx @@ -27,7 +27,9 @@ Registry items include: - `description`: short summary for search, agents, and docs. - `type`: registry item kind. - `category`: component category. -- `files`: source files installed by the CLI. +- `platforms`: non-empty renderer list, using `web`, `native`, or both. +- `native`: experimental package, channel, status, and parity metadata when `native` is supported. +- `files`: source files installed by the web CLI. - `dependencies`: npm dependencies required by the component. - `registryDependencies`: other registry components needed at runtime. - `version` and `stability`: release metadata stamped during registry build. @@ -42,4 +44,4 @@ The registry app generates shims and metadata from the package source. Do not ha ## Agent usage -Agents should prefer `/r/registry.json` for discovery and `/r/.json` for implementation details. Use `/llms-full.txt` when a single text context is easier than fetching many JSON endpoints. +Agents should prefer `/r/registry.json` for discovery and `/r/.json` for implementation details. Check `platforms` before selecting an installation path. Web descriptors install through shadcn; native pilot components install from `@vllnt/ui-native@canary`. Use `/llms-full.txt` when a single text context is easier than fetching many JSON endpoints. diff --git a/apps/registry/content/pages/docs/registry/fr.mdx b/apps/registry/content/pages/docs/registry/fr.mdx index 3a62c301..8937a0af 100644 --- a/apps/registry/content/pages/docs/registry/fr.mdx +++ b/apps/registry/content/pages/docs/registry/fr.mdx @@ -27,7 +27,9 @@ Les elements du registre incluent : - `description` : resume court pour la recherche, les agents et la documentation. - `type` : type d'element du registre. - `category` : categorie du composant. -- `files` : fichiers source installes par la CLI. +- `platforms` : liste non vide de renderers, avec `web`, `native` ou les deux. +- `native` : metadonnees experimentales de paquet, canal, statut et parite quand `native` est pris en charge. +- `files` : fichiers source installes par la CLI web. - `dependencies` : dependances npm requises par le composant. - `registryDependencies` : autres composants du registre necessaires a l'execution. - `version` et `stability` : metadonnees de version apposees pendant la construction du registre. @@ -42,4 +44,4 @@ L'application du registre genere des shims et des metadonnees a partir du code s ## Utilisation par les agents -Les agents devraient preferer `/r/registry.json` pour la decouverte et `/r/[name].json` pour les details d'implementation. Utilisez `/llms-full.txt` lorsqu'un seul contexte texte est plus pratique que la recuperation de nombreux points de terminaison JSON. +Les agents devraient preferer `/r/registry.json` pour la decouverte et `/r/[name].json` pour les details d'implementation. Verifiez `platforms` avant de choisir une methode d'installation. Les descripteurs web s'installent avec shadcn ; les composants pilotes natifs s'installent depuis `@vllnt/ui-native@canary`. Utilisez `/llms-full.txt` lorsqu'un seul contexte texte est plus pratique que la recuperation de nombreux points de terminaison JSON. diff --git a/apps/registry/e2e/i18n.spec.ts b/apps/registry/e2e/i18n.spec.ts index d9099059..7e7c0f23 100644 --- a/apps/registry/e2e/i18n.spec.ts +++ b/apps/registry/e2e/i18n.spec.ts @@ -16,6 +16,7 @@ const ROUTES = [ "/families/ai", "/docs", "/docs/installation", + "/docs/native", "/docs/theming", "/philosophy", "/templates", diff --git a/apps/registry/e2e/platforms.spec.ts b/apps/registry/e2e/platforms.spec.ts new file mode 100644 index 00000000..d71c469e --- /dev/null +++ b/apps/registry/e2e/platforms.spec.ts @@ -0,0 +1,60 @@ +import { expect, test } from "@playwright/test"; + +const nativeComponents = ["badge", "button", "card", "heading", "text"]; + +test.describe("platform-aware component discovery", () => { + test("filters the catalog to native-capable components", async ({ page }) => { + await page.goto("/components?platform=native"); + + const main = page.locator("main"); + await expect( + main.getByRole("link", { name: "Native", exact: true }), + ).toHaveAttribute("aria-current", "page"); + + for (const component of nativeComponents) { + await expect( + main.locator(`a[href="/components/${component}"]`), + ).toHaveCount(1); + } + await expect(main.locator('a[href="/components/accordion"]')).toHaveCount( + 0, + ); + await expect(main.getByText("Native · Experimental")).toHaveCount(5); + }); + + test("shows native status and installation on a portable component", async ({ + page, + }) => { + await page.goto("/components/button"); + + const main = page.locator("main"); + await expect( + main + .getByLabel("Supported platforms") + .first() + .getByText("Native · Experimental"), + ).toBeVisible(); + await expect( + main.getByRole("heading", { name: "React Native installation" }), + ).toBeVisible(); + await expect( + main.getByText("pnpm add @vllnt/ui-native@canary"), + ).toBeVisible(); + }); + + test("keeps web-only components labeled and unfiltered by default", async ({ + page, + }) => { + await page.goto("/components/accordion"); + + const main = page.locator("main"); + const headerPlatforms = main.getByLabel("Supported platforms").first(); + await expect(headerPlatforms.getByText("Web", { exact: true })).toBeVisible(); + await expect(headerPlatforms.getByText("Native · Experimental")).toHaveCount( + 0, + ); + await expect( + main.getByRole("heading", { name: "React Native installation" }), + ).toHaveCount(0); + }); +}); diff --git a/apps/registry/lib/component-metadata.json b/apps/registry/lib/component-metadata.json index 91b8df8f..1b3d6ac8 100644 --- a/apps/registry/lib/component-metadata.json +++ b/apps/registry/lib/component-metadata.json @@ -4,6 +4,9 @@ "defaultStoryId": "content-accordion--default", "description": "Collapsible content sections supporting single or multiple open items.", "name": "accordion", + "platforms": [ + "web" + ], "stories": [ { "id": "content-accordion--default", @@ -17,6 +20,9 @@ "defaultStoryId": "data-activityheatmap--default", "description": "Contribution-style grid for visualizing operational activity over time.", "name": "activity-heatmap", + "platforms": [ + "web" + ], "stories": [ { "id": "data-activityheatmap--default", @@ -38,6 +44,9 @@ "defaultStoryId": "analytics-activitylog--default", "description": "Paginated activity feed for audit history and analytics changes.", "name": "activity-log", + "platforms": [ + "web" + ], "stories": [ { "id": "analytics-activitylog--default", @@ -55,6 +64,9 @@ "defaultStoryId": "ai-agentactivity--default", "description": "Visual display of an AI agent's execution chain — steps, tools, decisions, progress.", "name": "agent-activity", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-agentactivity--default", @@ -80,6 +92,9 @@ "defaultStoryId": "ai-aiartifact--default", "description": "Rendered output area for AI-generated content with toolbar, copy/edit/download/fullscreen actions, and version chips.", "name": "ai-artifact", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-aiartifact--default", @@ -105,6 +120,9 @@ "defaultStoryId": "ai-chatinput--default", "description": "Prompt composer for conversational interfaces with helper text, toolbar actions, and submit states.", "name": "ai-chat-input", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-chatinput--default", @@ -122,6 +140,9 @@ "defaultStoryId": "ai-messagebubble--default", "description": "Chat bubble for assistant, user, tool, and system messages using the current design system surfaces.", "name": "ai-message-bubble", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-messagebubble--default", @@ -143,6 +164,9 @@ "defaultStoryId": "ai-aisidebar--default", "description": "Slide-out AI assistant panel with provider, header / content / footer slots, and a standalone trigger.", "name": "ai-sidebar", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-aisidebar--default", @@ -164,6 +188,9 @@ "defaultStoryId": "ai-sourcecitation--default", "description": "Compact source reference card for AI answers with a title, origin label, and optional excerpt.", "name": "ai-source-citation", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-sourcecitation--default", @@ -177,6 +204,9 @@ "defaultStoryId": "ai-streamingtext--default", "description": "Readable text block for partial assistant output with an optional live cursor while tokens stream in.", "name": "ai-streaming-text", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-streamingtext--default", @@ -194,6 +224,9 @@ "defaultStoryId": "ai-toolcalldisplay--default", "description": "Structured card for tool invocation traces, statuses, serialized input, and returned output.", "name": "ai-tool-call-display", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-toolcalldisplay--default", @@ -211,6 +244,9 @@ "defaultStoryId": "data-alert--default", "description": "Displays an alert message to the user.", "name": "alert", + "platforms": [ + "web" + ], "stories": [ { "id": "data-alert--default", @@ -228,6 +264,9 @@ "defaultStoryId": "overlay-alertdialog--default", "description": "Modal dialog for confirming destructive or important actions.", "name": "alert-dialog", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-alertdialog--default", @@ -241,6 +280,9 @@ "defaultStoryId": "canvas-alertpulse--default", "description": "Pulsing ring overlay for alerting canvas objects, with severity tones.", "name": "alert-pulse", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-alertpulse--default", @@ -266,6 +308,9 @@ "defaultStoryId": "canvas-anchorport--default", "description": "Port marker for object inputs, outputs, and bidirectional links on the canvas.", "name": "anchor-port", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-anchorport--default", @@ -279,6 +324,9 @@ "defaultStoryId": "effects-animatedbeam--default", "description": "Animated gradient beam that connects two elements with a flowing light path.", "name": "animated-beam", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-animatedbeam--default", @@ -292,6 +340,9 @@ "defaultStoryId": "effects-animatedgridpattern--default", "description": "Decorative grid background with squares that fade in and out at random.", "name": "animated-grid-pattern", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-animatedgridpattern--default", @@ -309,6 +360,9 @@ "defaultStoryId": "effects-animatedlist--default", "description": "List whose items animate in sequentially with a staggered entrance.", "name": "animated-list", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-animatedlist--default", @@ -322,6 +376,9 @@ "defaultStoryId": "effects-animatedtabs--default", "description": "Tabs with a sliding indicator that animates to the active item.", "name": "animated-tabs", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-animatedtabs--default", @@ -339,6 +396,9 @@ "defaultStoryId": "effects-animatedtestimonials--default", "description": "Testimonial carousel with animated transitions between quotes.", "name": "animated-testimonials", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-animatedtestimonials--default", @@ -356,6 +416,9 @@ "defaultStoryId": "utility-animatedtext--terminal", "description": "Staggered text reveal for headings, pull quotes, and short supporting copy.", "name": "animated-text", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-animatedtext--terminal", @@ -413,6 +476,9 @@ "defaultStoryId": "effects-animatedtooltip--default", "description": "Tooltip that animates in on hover or focus with a scale-and-fade.", "name": "animated-tooltip", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-animatedtooltip--default", @@ -430,6 +496,9 @@ "defaultStoryId": "learning-annotation--default", "description": "Inline highlight with an attached contextual note.", "name": "annotation", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-annotation--default", @@ -447,6 +516,9 @@ "defaultStoryId": "", "description": "Renders an area chart for data visualization.", "name": "area-chart", + "platforms": [ + "web" + ], "stories": [], "title": "Area Chart" }, @@ -455,6 +527,9 @@ "defaultStoryId": "utility-aspectratio--default", "description": "Constrains content to a specified aspect ratio.", "name": "aspect-ratio", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-aspectratio--default", @@ -468,6 +543,9 @@ "defaultStoryId": "billing-autoreload--default", "description": "Toggle + collapsible threshold/amount form for automatic credit reloading with locale-aware currency.", "name": "auto-reload", + "platforms": [ + "web" + ], "stories": [ { "id": "billing-autoreload--default", @@ -497,6 +575,9 @@ "defaultStoryId": "core-avatar--default", "description": "Displays a user avatar image with fallback initials.", "name": "avatar", + "platforms": [ + "web" + ], "stories": [ { "id": "core-avatar--default", @@ -510,6 +591,9 @@ "defaultStoryId": "data-avatargroup--default", "description": "Overlapping avatar stack for participants, assignees, and collaborative contexts.", "name": "avatar-group", + "platforms": [ + "web" + ], "stories": [ { "id": "data-avatargroup--default", @@ -527,6 +611,10 @@ "defaultStoryId": "core-badge--default", "description": "Small status label with variant styles.", "name": "badge", + "platforms": [ + "web", + "native" + ], "stories": [ { "id": "core-badge--default", @@ -552,6 +640,9 @@ "defaultStoryId": "core-banner--default", "description": "Full-width announcement bar with variants, dismissal, and an optional action slot.", "name": "banner", + "platforms": [ + "web" + ], "stories": [ { "id": "core-banner--default", @@ -585,6 +676,9 @@ "defaultStoryId": "", "description": "Renders a bar chart for data visualization.", "name": "bar-chart", + "platforms": [ + "web" + ], "stories": [], "title": "Bar Chart" }, @@ -593,6 +687,9 @@ "defaultStoryId": "effects-bentogrid--default", "description": "Responsive bento-style grid layout with feature cards.", "name": "bento-grid", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-bentogrid--default", @@ -606,6 +703,9 @@ "defaultStoryId": "content-blogcard--default", "description": "Card layout for displaying blog post previews.", "name": "blog-card", + "platforms": [ + "web" + ], "stories": [ { "id": "content-blogcard--default", @@ -619,6 +719,9 @@ "defaultStoryId": "effects-blurreveal--default", "description": "Reveals content with a blur-to-sharp transition when it scrolls into view.", "name": "blur-reveal", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-blurreveal--default", @@ -636,6 +739,9 @@ "defaultStoryId": "utility-borderbeam--default", "description": "Animated accent beam that travels around the border of a highlighted surface.", "name": "border-beam", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-borderbeam--default", @@ -653,6 +759,9 @@ "defaultStoryId": "canvas-bottomactivitystrip--default", "description": "Slim horizontally-scrolling row of recent canvas events for low-noise live activity.", "name": "bottom-activity-strip", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-bottomactivitystrip--default", @@ -678,6 +787,9 @@ "defaultStoryId": "layout-bottombar--default", "description": "Slim chrome strip pinned to the bottom of a canvas shell as a calm host for activity strips, status pills, and ambient indicators.", "name": "bottom-bar", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-bottombar--default", @@ -691,6 +803,9 @@ "defaultStoryId": "navigation-breadcrumb--default", "description": "Navigation breadcrumb trail showing the current page hierarchy.", "name": "breadcrumb", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-breadcrumb--default", @@ -704,6 +819,10 @@ "defaultStoryId": "core-button--default", "description": "Interactive button with multiple variants and sizes.", "name": "button", + "platforms": [ + "web", + "native" + ], "stories": [ { "id": "core-button--default", @@ -749,6 +868,9 @@ "defaultStoryId": "form-buttongroup--default", "description": "Visually connected group of buttons that share borders.", "name": "button-group", + "platforms": [ + "web" + ], "stories": [ { "id": "form-buttongroup--default", @@ -766,6 +888,9 @@ "defaultStoryId": "form-calendar--default", "description": "Date picker calendar for selecting dates.", "name": "calendar", + "platforms": [ + "web" + ], "stories": [ { "id": "form-calendar--default", @@ -779,6 +904,9 @@ "defaultStoryId": "content-callout--default", "description": "Highlighted content block with variant styles for info, warning, danger, and more.", "name": "callout", + "platforms": [ + "web" + ], "stories": [ { "id": "content-callout--default", @@ -792,6 +920,9 @@ "defaultStoryId": "data-candlestickchart--default", "description": "OHLC financial chart for session-by-session price action.", "name": "candlestick-chart", + "platforms": [ + "web" + ], "stories": [ { "id": "data-candlestickchart--default", @@ -813,6 +944,9 @@ "defaultStoryId": "layout-canvasshell--default", "description": "Layout shell for canvas workspaces with top bar, left rail, right dock, and bottom slot regions.", "name": "canvas-shell", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-canvasshell--default", @@ -826,6 +960,9 @@ "defaultStoryId": "layout-canvasview--default", "description": "Interactive pan-and-zoom viewport for spatial surfaces with keyboard, wheel, and overlay support.", "name": "canvas-view", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-canvasview--default", @@ -839,6 +976,10 @@ "defaultStoryId": "content-card--default", "description": "Container with header, content, and footer sections.", "name": "card", + "platforms": [ + "web", + "native" + ], "stories": [ { "id": "content-card--default", @@ -852,6 +993,9 @@ "defaultStoryId": "effects-cardflip--default", "description": "Card that flips in 3D between a front and back face.", "name": "card-flip", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-cardflip--default", @@ -869,6 +1013,9 @@ "defaultStoryId": "content-carousel--default", "description": "Scrollable content carousel with navigation controls.", "name": "carousel", + "platforms": [ + "web" + ], "stories": [ { "id": "content-carousel--default", @@ -882,6 +1029,9 @@ "defaultStoryId": "form-categoryfilter--default", "description": "Filterable category selection for content lists.", "name": "category-filter", + "platforms": [ + "web" + ], "stories": [ { "id": "form-categoryfilter--default", @@ -895,6 +1045,9 @@ "defaultStoryId": "ai-chainofthought--default", "description": "Ordered, status-aware visualization of a model's chain of thought with per-step state.", "name": "chain-of-thought", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-chainofthought--default", @@ -916,6 +1069,9 @@ "defaultStoryId": "layout-chatdocksection--default", "description": "Sidebar section that hosts a compact, scrollable chat thread alongside a spatial workspace — slotted into a dock or rail to keep an AI assistant in view.", "name": "chat-dock-section", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-chatdocksection--default", @@ -929,6 +1085,9 @@ "defaultStoryId": "core-checkbox--default", "description": "Toggle control for boolean input.", "name": "checkbox", + "platforms": [ + "web" + ], "stories": [ { "id": "core-checkbox--default", @@ -942,6 +1101,9 @@ "defaultStoryId": "form-checkboxgroup--default", "description": "Group of related checkboxes backed by an array of selected values.", "name": "checkbox-group", + "platforms": [ + "web" + ], "stories": [ { "id": "form-checkboxgroup--default", @@ -963,6 +1125,9 @@ "defaultStoryId": "learning-checklist--default", "description": "Interactive checklist with progress tracking and toggleable items.", "name": "checklist", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-checklist--default", @@ -980,6 +1145,9 @@ "defaultStoryId": "maps-choroplethmap--default", "description": "Standalone SVG choropleth — region polygons shaded by data value with tooltip, legend, and accessible data-table fallback.", "name": "choropleth-map", + "platforms": [ + "web" + ], "stories": [ { "id": "maps-choroplethmap--default", @@ -1005,6 +1173,9 @@ "defaultStoryId": "educational-chronologicaltimeline--default", "description": "Media-rich, scroll-driven chronological timeline with alternating cards, image/video/audio media, and a progress strip.", "name": "chronological-timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-chronologicaltimeline--default", @@ -1030,6 +1201,9 @@ "defaultStoryId": "educational-civilizationcard--default", "description": "Civilization overview with hero band, BCE/CE era timeline, key stats, achievements, and notable leaders.", "name": "civilization-card", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-civilizationcard--default", @@ -1059,6 +1233,9 @@ "defaultStoryId": "content-codeblock--default", "description": "Syntax-highlighted code display with copy support.", "name": "code-block", + "platforms": [ + "web" + ], "stories": [ { "id": "content-codeblock--default", @@ -1072,6 +1249,9 @@ "defaultStoryId": "content-codeplayground--default", "description": "Interactive code editor with live preview.", "name": "code-playground", + "platforms": [ + "web" + ], "stories": [ { "id": "content-codeplayground--default", @@ -1089,6 +1269,9 @@ "defaultStoryId": "content-collapsible--default", "description": "Expandable and collapsible content section.", "name": "collapsible", + "platforms": [ + "web" + ], "stories": [ { "id": "content-collapsible--default", @@ -1102,6 +1285,9 @@ "defaultStoryId": "form-colorpicker--default", "description": "Popover color picker with swatches, a hue slider, and a hex input.", "name": "color-picker", + "platforms": [ + "web" + ], "stories": [ { "id": "form-colorpicker--default", @@ -1119,6 +1305,9 @@ "defaultStoryId": "form-combobox--default", "description": "Searchable select input for choosing from a list of options.", "name": "combobox", + "platforms": [ + "web" + ], "stories": [ { "id": "form-combobox--default", @@ -1136,6 +1325,9 @@ "defaultStoryId": "overlay-command--default", "description": "Command palette with search, groups, and keyboard navigation.", "name": "command", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-command--default", @@ -1149,6 +1341,9 @@ "defaultStoryId": "canvas-commentpin--default", "description": "Anchored discussion pin rendered at canvas coordinates with author + unread badge.", "name": "comment-pin", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-commentpin--default", @@ -1174,6 +1369,9 @@ "defaultStoryId": "data-comparison--default", "description": "Side-by-side comparison layout for content or features.", "name": "comparison", + "platforms": [ + "web" + ], "stories": [ { "id": "data-comparison--default", @@ -1191,6 +1389,9 @@ "defaultStoryId": "learning-completiondialog--default", "description": "Dialog displayed upon completing a task or workflow.", "name": "completion-dialog", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-completiondialog--default", @@ -1208,6 +1409,9 @@ "defaultStoryId": "canvas-connectoredge--default", "description": "Curved edge between canvas objects with optional inline label state.", "name": "connector-edge", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-connectoredge--default", @@ -1221,6 +1425,9 @@ "defaultStoryId": "content-contentintro--default", "description": "Introductory section for content pages with title and description.", "name": "content-intro", + "platforms": [ + "web" + ], "stories": [ { "id": "content-contentintro--default", @@ -1238,6 +1445,9 @@ "defaultStoryId": "canvas-contextlens--default", "description": "Vignette overlay that dims the canvas outside a circular focus region.", "name": "context-lens", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-contextlens--default", @@ -1263,6 +1473,9 @@ "defaultStoryId": "overlay-contextmenu--default", "description": "Right-click context menu with items and submenus.", "name": "context-menu", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-contextmenu--default", @@ -1276,6 +1489,9 @@ "defaultStoryId": "data-contributiongraph--default", "description": "GitHub-style heatmap of daily activity over time.", "name": "contribution-graph", + "platforms": [ + "web" + ], "stories": [ { "id": "data-contributiongraph--default", @@ -1293,6 +1509,9 @@ "defaultStoryId": "ai-conversationthread--default", "description": "Compound component family for AI chat UIs that orchestrates auto-scroll, streaming indicators, empty states, and message rendering.", "name": "conversation-thread", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-conversationthread--default", @@ -1314,6 +1533,9 @@ "defaultStoryId": "utility-cookieconsent--default", "description": "Dismissible cookie-consent banner with positional variants and accept / reject actions for privacy compliance.", "name": "cookie-consent", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-cookieconsent--default", @@ -1327,6 +1549,9 @@ "defaultStoryId": "core-copybutton--default", "description": "Click-to-copy utility with confirmation feedback and a useCopyToClipboard hook.", "name": "copy-button", + "platforms": [ + "web" + ], "stories": [ { "id": "core-copybutton--default", @@ -1352,6 +1577,9 @@ "defaultStoryId": "data-countdowntimer--default", "description": "Countdown and SLA timer for deadlines, escalations, and response windows.", "name": "countdown-timer", + "platforms": [ + "web" + ], "stories": [ { "id": "data-countdowntimer--default", @@ -1369,6 +1597,9 @@ "defaultStoryId": "account-billing-creditbadge--default", "description": "Balance status pill for credits, wallet states, and billing health.", "name": "credit-badge", + "platforms": [ + "web" + ], "stories": [ { "id": "account-billing-creditbadge--default", @@ -1386,6 +1617,9 @@ "defaultStoryId": "learning-curriculum--default", "description": "Course-layout container for a sequence of lessons or modules with title, progress, optional intro, and a stack of lesson rows.", "name": "curriculum", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-curriculum--default", @@ -1403,6 +1637,9 @@ "defaultStoryId": "effects-cursor--default", "description": "Custom cursor follower that trails the pointer.", "name": "cursor", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-cursor--default", @@ -1420,6 +1657,9 @@ "defaultStoryId": "data-datalist--default", "description": "Semantic key-value metadata layout for displaying labels with values.", "name": "data-list", + "platforms": [ + "web" + ], "stories": [ { "id": "data-datalist--default", @@ -1437,6 +1677,9 @@ "defaultStoryId": "data-datatable--default", "description": "Enhanced data table with sorting, filtering, selection, and pagination controls.", "name": "data-table", + "platforms": [ + "web" + ], "stories": [ { "id": "data-datatable--default", @@ -1450,6 +1693,9 @@ "defaultStoryId": "form-datefield--default", "description": "Native date input styled to match the design system.", "name": "date-field", + "platforms": [ + "web" + ], "stories": [ { "id": "form-datefield--default", @@ -1467,6 +1713,9 @@ "defaultStoryId": "form-datepicker--default", "description": "Single-date picker built with the shared calendar and popover primitives.", "name": "date-picker", + "platforms": [ + "web" + ], "stories": [ { "id": "form-datepicker--default", @@ -1484,6 +1733,9 @@ "defaultStoryId": "form-daterangepicker--default", "description": "Popover calendar for selecting a start and end date.", "name": "date-range-picker", + "platforms": [ + "web" + ], "stories": [ { "id": "form-daterangepicker--default", @@ -1501,6 +1753,9 @@ "defaultStoryId": "overlay-dialog--default", "description": "Modal dialog overlay for focused content and actions.", "name": "dialog", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-dialog--default", @@ -1514,6 +1769,9 @@ "defaultStoryId": "core-display--default", "description": "Oversized hero/display text driven by the display tokens, with an optional reduced-motion-safe reveal.", "name": "display", + "platforms": [ + "web" + ], "stories": [ { "id": "core-display--default", @@ -1535,6 +1793,9 @@ "defaultStoryId": "effects-dock--default", "description": "macOS-style dock whose icons magnify near the pointer.", "name": "dock", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-dock--default", @@ -1548,6 +1809,9 @@ "defaultStoryId": "content-documentsiblingnav--default", "description": "Newer/older navigator: links to the previous and next item in an ordered series.", "name": "document-sibling-nav", + "platforms": [ + "web" + ], "stories": [ { "id": "content-documentsiblingnav--default", @@ -1581,6 +1845,9 @@ "defaultStoryId": "effects-dotpattern--default", "description": "Decorative dotted background pattern built from token colors.", "name": "dot-pattern", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-dotpattern--default", @@ -1598,6 +1865,9 @@ "defaultStoryId": "overlay-drawer--default", "description": "Slide-out panel anchored to the screen edge.", "name": "drawer", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-drawer--default", @@ -1611,6 +1881,9 @@ "defaultStoryId": "overlay-dropdownmenu--default", "description": "Accessible dropdown menu with items, checkboxes, radio groups, and submenus.", "name": "dropdown-menu", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-dropdownmenu--default", @@ -1624,6 +1897,9 @@ "defaultStoryId": "canvas-edgelabel--default", "description": "Inline edge label for relationship semantics such as streams, handoffs, or policies.", "name": "edge-label", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-edgelabel--default", @@ -1637,6 +1913,9 @@ "defaultStoryId": "core-emptystate--default", "description": "Centered placeholder for empty lists, tables, and search results with sm/md/lg sizes.", "name": "empty-state", + "platforms": [ + "web" + ], "stories": [ { "id": "core-emptystate--default", @@ -1662,6 +1941,9 @@ "defaultStoryId": "educational-eracomparison--default", "description": "Side-by-side comparison of historical eras with domain rows, color-themed columns, highlights, and figure chips.", "name": "era-comparison", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-eracomparison--default", @@ -1683,6 +1965,9 @@ "defaultStoryId": "learning-exercise--default", "description": "Interactive exercise block for learning content.", "name": "exercise", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-exercise--default", @@ -1700,6 +1985,9 @@ "defaultStoryId": "effects-expandablecards--default", "description": "Cards that expand on click to reveal additional content.", "name": "expandable-cards", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-expandablecards--default", @@ -1713,6 +2001,9 @@ "defaultStoryId": "content-faq--default", "description": "Frequently asked questions section with expandable answers.", "name": "faq", + "platforms": [ + "web" + ], "stories": [ { "id": "content-faq--default", @@ -1730,6 +2021,9 @@ "defaultStoryId": "form-field--default", "description": "Layout wrapper pairing a label, control, description, and error message.", "name": "field", + "platforms": [ + "web" + ], "stories": [ { "id": "form-field--default", @@ -1751,6 +2045,9 @@ "defaultStoryId": "form-fieldset--default", "description": "Groups related fields under a shared legend.", "name": "fieldset", + "platforms": [ + "web" + ], "stories": [ { "id": "form-fieldset--default", @@ -1768,6 +2065,9 @@ "defaultStoryId": "form-fileupload--default", "description": "Dropzone-style file picker with previews for selected files.", "name": "file-upload", + "platforms": [ + "web" + ], "stories": [ { "id": "form-fileupload--default", @@ -1781,6 +2081,9 @@ "defaultStoryId": "form-filterbar--default", "description": "Horizontal bar with filter controls for content lists.", "name": "filter-bar", + "platforms": [ + "web" + ], "stories": [ { "id": "form-filterbar--default", @@ -1794,6 +2097,9 @@ "defaultStoryId": "learning-flashcard--default", "description": "Study card for active recall with prompt and answer states.", "name": "flashcard", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-flashcard--default", @@ -1807,6 +2113,9 @@ "defaultStoryId": "utility-floatingactionbutton--default", "description": "Fixed-position action button for primary actions.", "name": "floating-action-button", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-floatingactionbutton--default", @@ -1820,6 +2129,9 @@ "defaultStoryId": "effects-floatingnavbar--default", "description": "Floating navigation bar that hides on scroll down and reveals on scroll up.", "name": "floating-navbar", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-floatingnavbar--default", @@ -1833,6 +2145,9 @@ "defaultStoryId": "canvas-floatingtoolbar--default", "description": "Compact action bar that floats above a selection — primary / ghost / destructive variants.", "name": "floating-toolbar", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-floatingtoolbar--default", @@ -1854,6 +2169,9 @@ "defaultStoryId": "data-flowdiagram--default", "description": "Interactive flow diagram with nodes, edges, and controls.", "name": "flow-diagram", + "platforms": [ + "web" + ], "stories": [ { "id": "data-flowdiagram--default", @@ -1867,6 +2185,9 @@ "defaultStoryId": "canvas-followmode--default", "description": "Follow-mode chrome — outlines a region with a participant's color and surfaces a stop-following chip.", "name": "follow-mode", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-followmode--default", @@ -1888,6 +2209,9 @@ "defaultStoryId": "core-form--default", "description": "Validation wrapper for composing labels, descriptions, controls, and messages.", "name": "form", + "platforms": [ + "web" + ], "stories": [ { "id": "core-form--default", @@ -1905,6 +2229,9 @@ "defaultStoryId": "data-ganttchart--default", "description": "Project timeline with task bars, progress overlays, milestones, and a today indicator across day/week/month/quarter scales.", "name": "gantt-chart", + "platforms": [ + "web" + ], "stories": [ { "id": "data-ganttchart--default", @@ -1930,6 +2257,9 @@ "defaultStoryId": "data-gaugechart--default", "description": "Semicircular gauge for a single value within a range.", "name": "gauge-chart", + "platforms": [ + "web" + ], "stories": [ { "id": "data-gaugechart--default", @@ -1951,6 +2281,9 @@ "defaultStoryId": "educational-geographyquizmap--default", "description": "Interactive identify-mode map quiz — click the correct region per prompt with visual feedback, score, and results panel.", "name": "geography-quiz-map", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-geographyquizmap--default", @@ -1972,6 +2305,9 @@ "defaultStoryId": "effects-glasscard--default", "description": "Frosted-glass card surface with backdrop blur and a translucent background.", "name": "glass-card", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-glasscard--default", @@ -1985,6 +2321,9 @@ "defaultStoryId": "layout-glasspanel--default", "description": "Frosted-glass surface for floating chrome above a canvas — dock panels, hovering toolbars, and transient overlays — with a translucent background and soft border.", "name": "glass-panel", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-glasspanel--default", @@ -1998,6 +2337,9 @@ "defaultStoryId": "effects-glassprogress--default", "description": "Glass-styled progress bar with a translucent track and token-colored fill.", "name": "glass-progress", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-glassprogress--default", @@ -2015,6 +2357,9 @@ "defaultStoryId": "maps-globe3d--default", "description": "Standalone SVG pseudo-3D globe — orthographic projection with auto-rotation, drag interaction, lat/lng markers, and great-circle arcs.", "name": "globe-3d", + "platforms": [ + "web" + ], "stories": [ { "id": "maps-globe3d--default", @@ -2036,6 +2381,9 @@ "defaultStoryId": "core-grid--default", "description": "Responsive CSS grid layout primitive with breakpoint column and gap props.", "name": "grid", + "platforms": [ + "web" + ], "stories": [ { "id": "core-grid--default", @@ -2053,6 +2401,9 @@ "defaultStoryId": "canvas-grouphull--default", "description": "Durable boundary wrapper for related runtime objects sharing context or ownership.", "name": "group-hull", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-grouphull--default", @@ -2066,6 +2417,9 @@ "defaultStoryId": "canvas-handoffbeacon--default", "description": "Attention-routing beacon with pulsing ring and optional source / message card for live canvases.", "name": "handoff-beacon", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-handoffbeacon--default", @@ -2091,6 +2445,10 @@ "defaultStoryId": "core-heading--default", "description": "Semantic heading (h1–h6) with theme-overridable font-family, weight, and size design tokens.", "name": "heading", + "platforms": [ + "web", + "native" + ], "stories": [ { "id": "core-heading--default", @@ -2112,6 +2470,9 @@ "defaultStoryId": "maps-heatmapoverlay--default", "description": "Standalone SVG geographic heat map — radial-gradient blobs with configurable radius, blur, gradient, and opacity.", "name": "heat-map-overlay", + "platforms": [ + "web" + ], "stories": [ { "id": "maps-heatmapoverlay--default", @@ -2141,6 +2502,9 @@ "defaultStoryId": "canvas-heatoverlay--default", "description": "Heatmap-style overlay drawing soft radial blobs for canvas activity samples.", "name": "heat-overlay", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-heatoverlay--default", @@ -2166,6 +2530,9 @@ "defaultStoryId": "educational-historictimeline--default", "description": "Specialized timeline for historical events spanning centuries or millennia, with era bands, period bars, and BCE/CE point markers.", "name": "historic-timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-historictimeline--default", @@ -2191,6 +2558,9 @@ "defaultStoryId": "educational-historicalfigurecard--default", "description": "Profile card with portrait, lifespan timeline, fields, works, quote, connections, and an expandable biography section.", "name": "historical-figure-card", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-historicalfigurecard--default", @@ -2220,6 +2590,9 @@ "defaultStoryId": "navigation-horizontalscrollrow--default", "description": "Horizontal scroll container with snap scrolling, chevron navigation, and hidden scrollbar.", "name": "horizontal-scroll-row", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-horizontalscrollrow--default", @@ -2237,6 +2610,9 @@ "defaultStoryId": "overlay-hovercard--default", "description": "Card that appears on hover for previewing content.", "name": "hover-card", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-hovercard--default", @@ -2250,6 +2626,9 @@ "defaultStoryId": "canvas-infiniteplane--dot", "description": "Tiled pannable backdrop for the canvas with dot or grid pattern that drifts with the viewport.", "name": "infinite-plane", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-infiniteplane--dot", @@ -2275,6 +2654,9 @@ "defaultStoryId": "form-inlineinput--default", "description": "Inline text input with keyboard commit and cancel support.", "name": "inline-input", + "platforms": [ + "web" + ], "stories": [ { "id": "form-inlineinput--default", @@ -2288,6 +2670,9 @@ "defaultStoryId": "core-input--default", "description": "Text input field for forms.", "name": "input", + "platforms": [ + "web" + ], "stories": [ { "id": "core-input--default", @@ -2301,6 +2686,9 @@ "defaultStoryId": "form-inputgroup--leading-icon", "description": "Groups an input with leading or trailing addons.", "name": "input-group", + "platforms": [ + "web" + ], "stories": [ { "id": "form-inputgroup--leading-icon", @@ -2322,6 +2710,9 @@ "defaultStoryId": "form-inputotp--default", "description": "One-time password input with segmented fields.", "name": "input-otp", + "platforms": [ + "web" + ], "stories": [ { "id": "form-inputotp--default", @@ -2335,6 +2726,9 @@ "defaultStoryId": "timeline-interactivetimeline--default", "description": "Zoomable, pannable, multi-track timeline with category filter, today marker, and click-to-select events.", "name": "interactive-timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "timeline-interactivetimeline--default", @@ -2360,6 +2754,9 @@ "defaultStoryId": "form-item--default", "description": "Flexible row layout with leading media, content, and trailing actions.", "name": "item", + "platforms": [ + "web" + ], "stories": [ { "id": "form-item--default", @@ -2377,6 +2774,9 @@ "defaultStoryId": "canvas-jarvisdock--default", "description": "Floating bottom dock with quick-action buttons + a command-palette trigger.", "name": "jarvis-dock", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-jarvisdock--default", @@ -2402,6 +2802,9 @@ "defaultStoryId": "core-kbd--default", "description": "Keyboard key indicator with platform-aware modifier expansion via the shortcut prop.", "name": "kbd", + "platforms": [ + "web" + ], "stories": [ { "id": "core-kbd--default", @@ -2435,6 +2838,9 @@ "defaultStoryId": "learning-keyconcept--default", "description": "Highlighted definition block for key terms and a glossary list.", "name": "key-concept", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-keyconcept--default", @@ -2452,6 +2858,9 @@ "defaultStoryId": "learning-keyboardshortcutshelp--default", "description": "Displays available keyboard shortcuts to the user.", "name": "keyboard-shortcuts-help", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-keyboardshortcutshelp--default", @@ -2469,6 +2878,9 @@ "defaultStoryId": "educational-knowledgecheck--default", "description": "Inline knowledge check with multiple-choice / true-false / fill-blank questions, per-answer feedback, and a final score summary.", "name": "knowledge-check", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-knowledgecheck--default", @@ -2490,6 +2902,9 @@ "defaultStoryId": "utility-label--default", "description": "Accessible form label that associates descriptive text with an input control.", "name": "label", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-label--default", @@ -2503,6 +2918,9 @@ "defaultStoryId": "utility-langprovider--default", "description": "Context provider for language and internationalization.", "name": "lang-provider", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-langprovider--default", @@ -2516,6 +2934,9 @@ "defaultStoryId": "learning-learningobjectives--default", "description": "Lists learning goals for educational content.", "name": "learning-objectives", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-learningobjectives--default", @@ -2533,6 +2954,9 @@ "defaultStoryId": "layout-leftrail--default", "description": "Compact vertical rail for canvas modes, tool actions, and secondary navigation controls.", "name": "left-rail", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-leftrail--default", @@ -2546,6 +2970,9 @@ "defaultStoryId": "", "description": "Renders a line chart for data visualization.", "name": "line-chart", + "platforms": [ + "web" + ], "stories": [], "title": "Line Chart" }, @@ -2554,6 +2981,9 @@ "defaultStoryId": "core-link--default", "description": "Styled anchor with emphasis variants and an external-link affordance.", "name": "link", + "platforms": [ + "web" + ], "stories": [ { "id": "core-link--default", @@ -2579,6 +3009,9 @@ "defaultStoryId": "effects-liquidglass--default", "description": "Glass surface with an animated liquid gradient sheen.", "name": "liquid-glass", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-liquidglass--default", @@ -2592,6 +3025,9 @@ "defaultStoryId": "form-listbox--single-select", "description": "Accessible single- or multi-select list of options.", "name": "list-box", + "platforms": [ + "web" + ], "stories": [ { "id": "form-listbox--single-select", @@ -2609,6 +3045,9 @@ "defaultStoryId": "canvas-livecursor--default", "description": "Remote user's cursor rendered at canvas coordinates with name + status chip.", "name": "live-cursor", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-livecursor--default", @@ -2634,6 +3073,9 @@ "defaultStoryId": "data-livefeed--default", "description": "Rolling activity stream for surfacing incidents, deploys, and operational signals in real time.", "name": "live-feed", + "platforms": [ + "web" + ], "stories": [ { "id": "data-livefeed--default", @@ -2651,6 +3093,9 @@ "defaultStoryId": "effects-magnetic--default", "description": "Wrapper that pulls its content toward the pointer for a magnetic effect.", "name": "magnetic", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-magnetic--default", @@ -2668,6 +3113,9 @@ "defaultStoryId": "effects-magneticbutton--default", "description": "Button that drifts toward the pointer for a magnetic hover effect.", "name": "magnetic-button", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-magneticbutton--default", @@ -2685,6 +3133,9 @@ "defaultStoryId": "maps-map2d--default", "description": "Lightweight 2D map primitive — SVG canvas with equirectangular projection, markers, popups, GeoJSON polygon layers, zoom controls, and an optional backdrop image.", "name": "map-2d", + "platforms": [ + "web" + ], "stories": [ { "id": "maps-map2d--default", @@ -2710,6 +3161,9 @@ "defaultStoryId": "educational-maptimeline--default", "description": "Standalone SVG map + time slider — era polygons and year-pinned events appear as the user scrubs the timeline.", "name": "map-timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-maptimeline--default", @@ -2731,6 +3185,9 @@ "defaultStoryId": "data-markettreemap--default", "description": "Sector-style market heatmap using weighted tiles and signed performance colors.", "name": "market-treemap", + "platforms": [ + "web" + ], "stories": [ { "id": "data-markettreemap--default", @@ -2748,6 +3205,9 @@ "defaultStoryId": "utility-marquee--default", "description": "Continuously scrolling content lane for badges, logos, and status chips.", "name": "marquee", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-marquee--default", @@ -2777,6 +3237,9 @@ "defaultStoryId": "content-mdxcontent--default", "description": "Renders MDX content with component mapping.", "name": "mdx-content", + "platforms": [ + "web" + ], "stories": [ { "id": "content-mdxcontent--default", @@ -2790,6 +3253,9 @@ "defaultStoryId": "navigation-menubar--default", "description": "Horizontal menu bar with dropdown menus.", "name": "menubar", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-menubar--default", @@ -2803,6 +3269,9 @@ "defaultStoryId": "effects-meteors--default", "description": "Decorative meteor shower of streaks falling across the background.", "name": "meteors", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-meteors--default", @@ -2820,6 +3289,9 @@ "defaultStoryId": "core-meter--default", "description": "Static measurement bar (role=meter) for a known range, with optional segments.", "name": "meter", + "platforms": [ + "web" + ], "stories": [ { "id": "core-meter--default", @@ -2841,6 +3313,9 @@ "defaultStoryId": "canvas-metriccluster--default", "description": "Compact stack of related metrics pinned to a canvas object's corner.", "name": "metric-cluster", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-metriccluster--default", @@ -2866,6 +3341,9 @@ "defaultStoryId": "data-metricgauge--default", "description": "Real-time arc and dial display for monitored percentages and utilization.", "name": "metric-gauge", + "platforms": [ + "web" + ], "stories": [ { "id": "data-metricgauge--default", @@ -2883,6 +3361,9 @@ "defaultStoryId": "panels-minimappanel--default", "description": "Viewport overview panel showing canvas bounds, markers, and the current zoom window.", "name": "mini-map-panel", + "platforms": [ + "web" + ], "stories": [ { "id": "panels-minimappanel--default", @@ -2896,6 +3377,9 @@ "defaultStoryId": "ai-modelcomparison--default", "description": "Side-by-side comparison of AI model responses with optional blind mode, metadata stats, and a vote bar.", "name": "model-comparison", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-modelcomparison--default", @@ -2921,6 +3405,9 @@ "defaultStoryId": "form-modelselector--default", "description": "Dropdown selector for choosing AI models.", "name": "model-selector", + "platforms": [ + "web" + ], "stories": [ { "id": "form-modelselector--default", @@ -2934,6 +3421,9 @@ "defaultStoryId": "form-multiselect--default", "description": "Popover-based multi-selection input with selected-value badges and optional search.", "name": "multi-select", + "platforms": [ + "web" + ], "stories": [ { "id": "form-multiselect--default", @@ -2955,6 +3445,9 @@ "defaultStoryId": "canvas-multiselectlasso--default", "description": "Selection rectangle overlay for canvas multi-select gestures.", "name": "multi-select-lasso", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-multiselectlasso--default", @@ -2980,6 +3473,9 @@ "defaultStoryId": "form-nativeselect--default", "description": "Styled wrapper around the native select element.", "name": "native-select", + "platforms": [ + "web" + ], "stories": [ { "id": "form-nativeselect--default", @@ -2997,6 +3493,9 @@ "defaultStoryId": "navigation-navbarsaas--default", "description": "SaaS-style navigation bar with branding and links.", "name": "navbar-saas", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-navbarsaas--default", @@ -3010,6 +3509,9 @@ "defaultStoryId": "navigation-navigationmenu--default", "description": "Accessible navigation menu with links and sub-navigation.", "name": "navigation-menu", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-navigationmenu--default", @@ -3023,6 +3525,9 @@ "defaultStoryId": "forms-newslettersignup--default", "description": "Email-capture form with idle/sending/sent/error state machine, custom validators, and overridable labels.", "name": "newsletter-signup", + "platforms": [ + "web" + ], "stories": [ { "id": "forms-newslettersignup--default", @@ -3052,6 +3557,9 @@ "defaultStoryId": "form-numberinput--default", "description": "Numeric input with increment and decrement controls.", "name": "number-input", + "platforms": [ + "web" + ], "stories": [ { "id": "form-numberinput--default", @@ -3065,6 +3573,9 @@ "defaultStoryId": "utility-numberticker--default", "description": "Animated number transitions for stats, KPIs, and compact numeric callouts.", "name": "number-ticker", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-numberticker--default", @@ -3082,6 +3593,9 @@ "defaultStoryId": "canvas-objectcard--default", "description": "Durable object view for agents, runs, artifacts, and tasks inside the canvas.", "name": "object-card", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-objectcard--default", @@ -3095,6 +3609,9 @@ "defaultStoryId": "canvas-objecthandle--default", "description": "Drag/reposition affordance for spatial objects that need a calm grab target.", "name": "object-handle", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-objecthandle--default", @@ -3108,6 +3625,9 @@ "defaultStoryId": "canvas-objectinspector--default", "description": "Right-dock detail header — kind chip, status dot, title/subtitle, with property-section slots.", "name": "object-inspector", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-objectinspector--default", @@ -3133,6 +3653,9 @@ "defaultStoryId": "data-orderbook--default", "description": "Level II bid/ask depth ladder with cumulative size bars and spread readout.", "name": "order-book", + "platforms": [ + "web" + ], "stories": [ { "id": "data-orderbook--default", @@ -3154,6 +3677,9 @@ "defaultStoryId": "layout-overviewboard--default", "description": "Grid of overview cards for at-a-glance dashboards — a row of headline metrics with consistent spacing and tone.", "name": "overview-board", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-overviewboard--default", @@ -3167,6 +3693,9 @@ "defaultStoryId": "navigation-pagination--default", "description": "Page navigation controls with previous, next, and page links.", "name": "pagination", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-pagination--default", @@ -3180,6 +3709,9 @@ "defaultStoryId": "core-panel--default", "description": "Bordered, titled content surface with header, body, and footer slots.", "name": "panel", + "platforms": [ + "web" + ], "stories": [ { "id": "core-panel--default", @@ -3197,6 +3729,9 @@ "defaultStoryId": "educational-paralleltimeline--default", "description": "Multi-track timeline with shared time axis, BCE/CE event markers, and optional era bands for comparative history.", "name": "parallel-timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-paralleltimeline--default", @@ -3222,6 +3757,9 @@ "defaultStoryId": "effects-particles--default", "description": "Floating particle field that drifts gently in the background.", "name": "particles", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-particles--default", @@ -3239,6 +3777,9 @@ "defaultStoryId": "form-passwordinput--default", "description": "Password field with a built-in visibility toggle.", "name": "password-input", + "platforms": [ + "web" + ], "stories": [ { "id": "form-passwordinput--default", @@ -3252,6 +3793,9 @@ "defaultStoryId": "form-phoneinput--default", "description": "Phone number input with a country dialing-code selector.", "name": "phone-input", + "platforms": [ + "web" + ], "stories": [ { "id": "form-phoneinput--default", @@ -3269,6 +3813,9 @@ "defaultStoryId": "data-piechart--default", "description": "Proportional pie or donut chart for part-to-whole comparisons.", "name": "pie-chart", + "platforms": [ + "web" + ], "stories": [ { "id": "data-piechart--default", @@ -3290,6 +3837,9 @@ "defaultStoryId": "account-billing-planbadge--default", "description": "Subscription tier indicator for pricing, billing, and account summaries.", "name": "plan-badge", + "platforms": [ + "web" + ], "stories": [ { "id": "account-billing-planbadge--default", @@ -3311,6 +3861,9 @@ "defaultStoryId": "canvas-playbackghost--default", "description": "Translucent overlay marking where a canvas object was at a previous timestamp during playback.", "name": "playback-ghost", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-playbackghost--default", @@ -3336,6 +3889,9 @@ "defaultStoryId": "canvas-policydeliverypanel--default", "description": "Right-dock panel listing policies / guardrails active for the route or run.", "name": "policy-delivery-panel", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-policydeliverypanel--default", @@ -3361,6 +3917,9 @@ "defaultStoryId": "overlay-popover--default", "description": "Floating content panel anchored to a trigger element.", "name": "popover", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-popover--default", @@ -3374,6 +3933,9 @@ "defaultStoryId": "canvas-presencestack--default", "description": "Overlapping live-presence avatars with status dots and a sane overflow chip.", "name": "presence-stack", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-presencestack--default", @@ -3399,6 +3961,9 @@ "defaultStoryId": "canvas-presencesyncindicator--live", "description": "Compact pill that surfaces live connection + sync health for a collaborative canvas.", "name": "presence-sync-indicator", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-presencesyncindicator--live", @@ -3424,6 +3989,9 @@ "defaultStoryId": "billing-pricingtable--default", "description": "Plan comparison with feature checklist, tier highlighting, CTA, and an optional monthly/annual toggle.", "name": "pricing-table", + "platforms": [ + "web" + ], "stories": [ { "id": "billing-pricingtable--default", @@ -3445,6 +4013,9 @@ "defaultStoryId": "educational-primarysourceviewer--default", "description": "Document viewer for historical primary sources with zoom, rotate, region annotations, transcription panel, and metadata footer.", "name": "primary-source-viewer", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-primarysourceviewer--default", @@ -3466,6 +4037,9 @@ "defaultStoryId": "learning-protip--default", "description": "Highlighted tip block with variants for tips, best practices, gotchas, and more.", "name": "pro-tip", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-protip--default", @@ -3479,6 +4053,9 @@ "defaultStoryId": "learning-profilesection--default", "description": "User profile display section with avatar and details.", "name": "profile-section", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-profilesection--default", @@ -3496,6 +4073,9 @@ "defaultStoryId": "data-progressbar--default", "description": "Visual progress indicator with labels and completion state.", "name": "progress-bar", + "platforms": [ + "web" + ], "stories": [ { "id": "data-progressbar--default", @@ -3509,6 +4089,9 @@ "defaultStoryId": "data-progresscard--default", "description": "Card displaying progress metrics and status.", "name": "progress-card", + "platforms": [ + "web" + ], "stories": [ { "id": "data-progresscard--default", @@ -3522,6 +4105,9 @@ "defaultStoryId": "learning-progresstracker--default", "description": "Curriculum-level learning dashboard for modules, lessons, exercises, streaks, and earned skills.", "name": "progress-tracker", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-progresstracker--default", @@ -3539,6 +4125,9 @@ "defaultStoryId": "effects-progressiveblur--default", "description": "Progressive blur overlay that fades focus toward one edge.", "name": "progressive-blur", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-progressiveblur--default", @@ -3556,6 +4145,9 @@ "defaultStoryId": "ai-promptinput--default", "description": "Auto-growing prompt textarea with a submit affordance and optional toolbar slot.", "name": "prompt-input", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-promptinput--default", @@ -3581,6 +4173,9 @@ "defaultStoryId": "ai-prompttemplates--default", "description": "Searchable prompt template gallery with category filter, variable fill-in form, and onSelect.", "name": "prompt-templates", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-prompttemplates--default", @@ -3606,6 +4201,9 @@ "defaultStoryId": "canvas-propertysection--default", "description": "Compact key/value grid for inspector panels — labels, sublabels, optional collapse.", "name": "property-section", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-propertysection--default", @@ -3631,6 +4229,9 @@ "defaultStoryId": "core-prose--article", "description": "Long-form content wrapper that styles raw HTML descendants with the token-driven type scale.", "name": "prose", + "platforms": [ + "web" + ], "stories": [ { "id": "core-prose--article", @@ -3644,6 +4245,9 @@ "defaultStoryId": "core-qrcode--default", "description": "Renders a QR code from a string value as a theme-aware SVG.", "name": "qr-code", + "platforms": [ + "web" + ], "stories": [ { "id": "core-qrcode--default", @@ -3665,6 +4269,9 @@ "defaultStoryId": "learning-quiz--default", "description": "Interactive multiple-choice quiz with hints, explanations, and scoring.", "name": "quiz", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-quiz--default", @@ -3682,6 +4289,9 @@ "defaultStoryId": "data-radarchart--default", "description": "Spider chart comparing several metrics on shared axes.", "name": "radar-chart", + "platforms": [ + "web" + ], "stories": [ { "id": "data-radarchart--default", @@ -3699,6 +4309,9 @@ "defaultStoryId": "core-radiogroup--default", "description": "Group of radio buttons for single-selection input.", "name": "radio-group", + "platforms": [ + "web" + ], "stories": [ { "id": "core-radiogroup--default", @@ -3712,6 +4325,9 @@ "defaultStoryId": "form-rangecalendar--default", "description": "Calendar that selects a start and end date as a range.", "name": "range-calendar", + "platforms": [ + "web" + ], "stories": [ { "id": "form-rangecalendar--default", @@ -3733,6 +4349,9 @@ "defaultStoryId": "learning-rating--default", "description": "Inline star rating for lightweight learner feedback.", "name": "rating", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-rating--default", @@ -3750,6 +4369,9 @@ "defaultStoryId": "ai-reasoning--default", "description": "Collapsible AI reasoning trace with streaming support and ordered or free-form steps.", "name": "reasoning", + "platforms": [ + "web" + ], "stories": [ { "id": "ai-reasoning--default", @@ -3771,6 +4393,9 @@ "defaultStoryId": "canvas-relationshipinspector--default", "description": "Right-dock panel listing inbound + outbound edges of the focused canvas object.", "name": "relationship-inspector", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-relationshipinspector--default", @@ -3796,6 +4421,9 @@ "defaultStoryId": "utility-resizable--default", "description": "Resizable panel layout with draggable handles.", "name": "resizable", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-resizable--default", @@ -3809,6 +4437,9 @@ "defaultStoryId": "effects-revealtext--default", "description": "Reveals text with a directional slide-and-fade when it enters the viewport.", "name": "reveal-text", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-revealtext--default", @@ -3830,6 +4461,9 @@ "defaultStoryId": "layout-rightdock--default", "description": "Context dock for inspectors, summaries, and secondary canvas panels.", "name": "right-dock", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-rightdock--default", @@ -3843,6 +4477,9 @@ "defaultStoryId": "account-billing-rolebadge--default", "description": "Account role indicator for owners, admins, members, and billing contacts.", "name": "role-badge", + "platforms": [ + "web" + ], "stories": [ { "id": "account-billing-rolebadge--default", @@ -3860,6 +4497,9 @@ "defaultStoryId": "maps-routemap--default", "description": "Standalone SVG map with animated route paths, waypoints, and progress indicator. For trade routes, voyages, migrations, delivery tracking.", "name": "route-map", + "platforms": [ + "web" + ], "stories": [ { "id": "maps-routemap--default", @@ -3889,6 +4529,9 @@ "defaultStoryId": "canvas-routingassignmentpanel--default", "description": "Right-dock panel listing the agent slots an active route dispatches to.", "name": "routing-assignment-panel", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-routingassignmentpanel--default", @@ -3914,6 +4557,9 @@ "defaultStoryId": "canvas-runtimeline--default", "description": "Multi-lane execution timeline showing run phases over time, with optional cursor.", "name": "run-timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-runtimeline--default", @@ -3939,6 +4585,9 @@ "defaultStoryId": "canvas-runtimeoverviewpanel--default", "description": "Top-of-dock summary tile grid for runtime health when no canvas object is selected.", "name": "runtime-overview-panel", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-runtimeoverviewpanel--default", @@ -3964,6 +4613,9 @@ "defaultStoryId": "data-sankeychart--default", "description": "Flow diagram showing weighted links between nodes.", "name": "sankey-chart", + "platforms": [ + "web" + ], "stories": [ { "id": "data-sankeychart--default", @@ -3977,6 +4629,9 @@ "defaultStoryId": "analytics-scopeselector--default", "description": "Multi-level scope picker for nested environments, teams, and targets.", "name": "scope-selector", + "platforms": [ + "web" + ], "stories": [ { "id": "analytics-scopeselector--default", @@ -3994,6 +4649,9 @@ "defaultStoryId": "effects-scrambletext--default", "description": "Scrambles characters then resolves them into the final text.", "name": "scramble-text", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-scrambletext--default", @@ -4011,6 +4669,9 @@ "defaultStoryId": "utility-scrollarea--default", "description": "Custom scrollable area with styled scrollbars.", "name": "scroll-area", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-scrollarea--default", @@ -4024,6 +4685,9 @@ "defaultStoryId": "effects-scrollprogress--default", "description": "Fixed bar that tracks reading progress down the page.", "name": "scroll-progress", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-scrollprogress--default", @@ -4037,6 +4701,9 @@ "defaultStoryId": "form-searchbar--default", "description": "Text search input with icon and clear functionality.", "name": "search-bar", + "platforms": [ + "web" + ], "stories": [ { "id": "form-searchbar--default", @@ -4050,6 +4717,9 @@ "defaultStoryId": "learning-searchdialog--default", "description": "Full-screen search dialog with keyboard navigation.", "name": "search-dialog", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-searchdialog--default", @@ -4067,6 +4737,9 @@ "defaultStoryId": "form-searchfield--default", "description": "Search input with a leading icon and a clear button.", "name": "search-field", + "platforms": [ + "web" + ], "stories": [ { "id": "form-searchfield--default", @@ -4084,6 +4757,9 @@ "defaultStoryId": "form-segmentedcontrol--default", "description": "Single-choice segmented selector for switching modes, views, or filters.", "name": "segmented-control", + "platforms": [ + "web" + ], "stories": [ { "id": "form-segmentedcontrol--default", @@ -4105,6 +4781,9 @@ "defaultStoryId": "form-select--default", "description": "Dropdown select input for choosing from a list of options.", "name": "select", + "platforms": [ + "web" + ], "stories": [ { "id": "form-select--default", @@ -4118,6 +4797,9 @@ "defaultStoryId": "canvas-selectionhalo--default", "description": "Local-user selection halo with corner handles + label slot for spatial canvases.", "name": "selection-halo", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-selectionhalo--default", @@ -4139,6 +4821,9 @@ "defaultStoryId": "canvas-selectionpresence--default", "description": "Dashed-border overlay marking what another user has selected on the canvas.", "name": "selection-presence", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-selectionpresence--default", @@ -4164,6 +4849,9 @@ "defaultStoryId": "core-separator--default", "description": "Visual divider between content sections.", "name": "separator", + "platforms": [ + "web" + ], "stories": [ { "id": "core-separator--default", @@ -4177,6 +4865,9 @@ "defaultStoryId": "data-severitybadge--default", "description": "Operational severity label with tone variants and optional pulse for critical incidents.", "name": "severity-badge", + "platforms": [ + "web" + ], "stories": [ { "id": "data-severitybadge--default", @@ -4206,6 +4897,9 @@ "defaultStoryId": "utility-sharedialog--default", "description": "Modal dialog for sharing a page across social platforms with a copy-link action and overridable labels.", "name": "share-dialog", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-sharedialog--default", @@ -4219,6 +4913,9 @@ "defaultStoryId": "content-sharesection--default", "description": "Social sharing buttons and link copy section.", "name": "share-section", + "platforms": [ + "web" + ], "stories": [ { "id": "content-sharesection--default", @@ -4236,6 +4933,9 @@ "defaultStoryId": "overlay-sheet--default", "description": "Slide-over panel from the edge of the screen.", "name": "sheet", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-sheet--default", @@ -4257,6 +4957,9 @@ "defaultStoryId": "effects-shimmerbutton--default", "description": "Button with a light sheen that sweeps across its surface.", "name": "shimmer-button", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-shimmerbutton--default", @@ -4274,6 +4977,9 @@ "defaultStoryId": "effects-shimmertext--default", "description": "Text with a bright light band that sweeps across it.", "name": "shimmer-text", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-shimmertext--default", @@ -4291,6 +4997,9 @@ "defaultStoryId": "effects-shineborder--default", "description": "Wrapper that draws an animated gradient border around its content.", "name": "shine-border", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-shineborder--default", @@ -4308,6 +5017,9 @@ "defaultStoryId": "effects-shinybutton--default", "description": "Button with a glossy gradient sheen that sweeps on a loop.", "name": "shiny-button", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-shinybutton--default", @@ -4321,6 +5033,9 @@ "defaultStoryId": "navigation-sidebar--default", "description": "Collapsible sidebar navigation layout.", "name": "sidebar", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-sidebar--default", @@ -4334,6 +5049,9 @@ "defaultStoryId": "navigation-sidebarprovider--default", "description": "Context provider for managing sidebar open/close state.", "name": "sidebar-provider", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-sidebarprovider--default", @@ -4347,6 +5065,9 @@ "defaultStoryId": "navigation-sidebartoggle--default", "description": "Responsive toggle button for opening and closing the sidebar.", "name": "sidebar-toggle", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-sidebartoggle--default", @@ -4360,6 +5081,9 @@ "defaultStoryId": "core-skeleton--default", "description": "Placeholder loading animation for content.", "name": "skeleton", + "platforms": [ + "web" + ], "stories": [ { "id": "core-skeleton--default", @@ -4373,6 +5097,9 @@ "defaultStoryId": "core-slider--default", "description": "Range slider input for selecting numeric values.", "name": "slider", + "platforms": [ + "web" + ], "stories": [ { "id": "core-slider--default", @@ -4386,6 +5113,9 @@ "defaultStoryId": "content-slideshow--default", "description": "Step-through slideshow for presenting content sequentially.", "name": "slideshow", + "platforms": [ + "web" + ], "stories": [ { "id": "content-slideshow--default", @@ -4403,6 +5133,9 @@ "defaultStoryId": "canvas-snapguides--default", "description": "Alignment guide overlay — dashed vertical and horizontal lines that surface during a drag.", "name": "snap-guides", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-snapguides--default", @@ -4424,6 +5157,9 @@ "defaultStoryId": "effects-sparkles--default", "description": "Decorative twinkling sparkles scattered behind content.", "name": "sparkles", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-sparkles--default", @@ -4441,6 +5177,9 @@ "defaultStoryId": "data-sparklinegrid--default", "description": "KPI grid of labeled value tiles each paired with a compact sparkline trend.", "name": "sparkline-grid", + "platforms": [ + "web" + ], "stories": [ { "id": "data-sparklinegrid--default", @@ -4458,6 +5197,9 @@ "defaultStoryId": "utility-spinner--default", "description": "Animated loading spinner indicator.", "name": "spinner", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-spinner--default", @@ -4471,6 +5213,9 @@ "defaultStoryId": "effects-spinningtext--default", "description": "Text laid out in a circle that rotates continuously.", "name": "spinning-text", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-spinningtext--default", @@ -4488,6 +5233,9 @@ "defaultStoryId": "effects-spotlightcard--default", "description": "Card with a radial spotlight that follows the pointer.", "name": "spotlight-card", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-spotlightcard--default", @@ -4501,6 +5249,9 @@ "defaultStoryId": "data-statcard--default", "description": "Headline KPI card for metrics, trends, and supporting context.", "name": "stat-card", + "platforms": [ + "web" + ], "stories": [ { "id": "data-statcard--default", @@ -4518,6 +5269,9 @@ "defaultStoryId": "canvas-statebadgeoverlay--default", "description": "State chip pinned to a canvas object's corner — idle, queued, running, complete, failed, stopped.", "name": "state-badge-overlay", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-statebadgeoverlay--default", @@ -4543,6 +5297,9 @@ "defaultStoryId": "data-statusboard--default", "description": "Service health grid for surfacing infrastructure state, queue pressure, and incidents.", "name": "status-board", + "platforms": [ + "web" + ], "stories": [ { "id": "data-statusboard--default", @@ -4564,6 +5321,9 @@ "defaultStoryId": "data-statusindicator--default", "description": "Compact status label with tone, variant, and activity dot options.", "name": "status-indicator", + "platforms": [ + "web" + ], "stories": [ { "id": "data-statusindicator--default", @@ -4581,6 +5341,9 @@ "defaultStoryId": "learning-stepbystep--default", "description": "Numbered step guide with optional interactive completion tracking.", "name": "step-by-step", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-stepbystep--default", @@ -4594,6 +5357,9 @@ "defaultStoryId": "navigation-stepnavigation--default", "description": "Navigation controls for stepping through multi-page content.", "name": "step-navigation", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-stepnavigation--default", @@ -4607,6 +5373,9 @@ "defaultStoryId": "learning-stepper--default", "description": "Sequenced steps with complete, current, and upcoming states.", "name": "stepper", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-stepper--default", @@ -4624,6 +5393,9 @@ "defaultStoryId": "canvas-stickymetric--default", "description": "Pinned metric pill that sticks to a canvas object's corner.", "name": "sticky-metric", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-stickymetric--default", @@ -4649,6 +5421,9 @@ "defaultStoryId": "educational-storymap--default", "description": "Standalone SVG scroll-driven narrative map — IntersectionObserver tracks the active chapter and the map shifts to its center + zoom.", "name": "story-map", + "platforms": [ + "web" + ], "stories": [ { "id": "educational-storymap--default", @@ -4670,6 +5445,9 @@ "defaultStoryId": "account-billing-subscriptioncard--default", "description": "Subscription status and management card for plan, renewal, and usage details.", "name": "subscription-card", + "platforms": [ + "web" + ], "stories": [ { "id": "account-billing-subscriptioncard--default", @@ -4687,6 +5465,9 @@ "defaultStoryId": "utility-switch--default", "description": "Toggle switch control for binary on/off state.", "name": "switch", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-switch--default", @@ -4700,6 +5481,9 @@ "defaultStoryId": "data-table--default", "description": "Styled data table with header, body, and footer sections.", "name": "table", + "platforms": [ + "web" + ], "stories": [ { "id": "data-table--default", @@ -4713,6 +5497,9 @@ "defaultStoryId": "utility-tableofcontents--default", "description": "Auto-generated table of contents from page headings.", "name": "table-of-contents", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-tableofcontents--default", @@ -4730,6 +5517,9 @@ "defaultStoryId": "utility-tableofcontentspanel--default", "description": "Side panel rendering a table of contents for page navigation.", "name": "table-of-contents-panel", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-tableofcontentspanel--default", @@ -4747,6 +5537,9 @@ "defaultStoryId": "navigation-tabs--default", "description": "Tabbed content panels with keyboard-accessible tab triggers.", "name": "tabs", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-tabs--default", @@ -4760,6 +5553,9 @@ "defaultStoryId": "form-taggroup--selectable", "description": "Set of tags supporting selection and removal.", "name": "tag-group", + "platforms": [ + "web" + ], "stories": [ { "id": "form-taggroup--selectable", @@ -4777,6 +5573,9 @@ "defaultStoryId": "form-tagsinput--default", "description": "Keyboard-friendly tag editor for adding and removing string values.", "name": "tags-input", + "platforms": [ + "web" + ], "stories": [ { "id": "form-tagsinput--default", @@ -4798,6 +5597,9 @@ "defaultStoryId": "content-terminal--default", "description": "Terminal-style display for command output.", "name": "terminal", + "platforms": [ + "web" + ], "stories": [ { "id": "content-terminal--default", @@ -4811,6 +5613,10 @@ "defaultStoryId": "core-text--default", "description": "Body-text primitive with token-driven sans family and a size/tone/weight scale; polymorphic via `as`.", "name": "text", + "platforms": [ + "web", + "native" + ], "stories": [ { "id": "core-text--default", @@ -4828,6 +5634,9 @@ "defaultStoryId": "effects-textanimate--default", "description": "Animates text in by word or character with configurable effects.", "name": "text-animate", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-textanimate--default", @@ -4853,6 +5662,9 @@ "defaultStoryId": "form-textfield--default", "description": "Labelled text input bundling description and error message.", "name": "text-field", + "platforms": [ + "web" + ], "stories": [ { "id": "form-textfield--default", @@ -4874,6 +5686,9 @@ "defaultStoryId": "effects-textreveal--default", "description": "Dims and brightens words as the text scrolls through the viewport.", "name": "text-reveal", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-textreveal--default", @@ -4887,6 +5702,9 @@ "defaultStoryId": "effects-textshimmer--default", "description": "Text with an animated gradient fill that shimmers.", "name": "text-shimmer", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-textshimmer--default", @@ -4904,6 +5722,9 @@ "defaultStoryId": "core-textarea--default", "description": "Multi-line text input field.", "name": "textarea", + "platforms": [ + "web" + ], "stories": [ { "id": "core-textarea--default", @@ -4917,6 +5738,9 @@ "defaultStoryId": "utility-themepresetprovider--default", "description": "Applies the persisted theme preset on mount and injects a no-flash script so the saved preset is set before paint.", "name": "theme-preset-provider", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-themepresetprovider--default", @@ -4930,6 +5754,9 @@ "defaultStoryId": "utility-themeprovider--default", "description": "Context provider for light/dark theme switching.", "name": "theme-provider", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-themeprovider--default", @@ -4943,6 +5770,9 @@ "defaultStoryId": "utility-themeswitcher--default", "description": "Compact swatch row for switching between built-in theme presets, kept in sync with every other consumer on the page.", "name": "theme-switcher", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-themeswitcher--default", @@ -4956,6 +5786,9 @@ "defaultStoryId": "utility-themetoggle--default", "description": "Button to toggle between light and dark themes.", "name": "theme-toggle", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-themetoggle--default", @@ -4969,6 +5802,9 @@ "defaultStoryId": "learning-thinkingblock--default", "description": "Collapsible block showing AI thinking/reasoning with streaming support.", "name": "thinking-block", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-thinkingblock--default", @@ -4982,6 +5818,9 @@ "defaultStoryId": "canvas-threadbubble--default", "description": "Expanded discussion bubble for an anchored canvas comment thread.", "name": "thread-bubble", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-threadbubble--default", @@ -5007,6 +5846,9 @@ "defaultStoryId": "canvas-thresholdring--default", "description": "Compact ring gauge expressing how close a value is to a threshold.", "name": "threshold-ring", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-thresholdring--default", @@ -5032,6 +5874,9 @@ "defaultStoryId": "data-tickertape--default", "description": "Marquee-style scrolling symbol tape with price and change badges.", "name": "ticker-tape", + "platforms": [ + "web" + ], "stories": [ { "id": "data-tickertape--default", @@ -5049,6 +5894,9 @@ "defaultStoryId": "effects-tiltcard--default", "description": "Card that tilts in 3D toward the pointer for a parallax hover.", "name": "tilt-card", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-tiltcard--default", @@ -5062,6 +5910,9 @@ "defaultStoryId": "form-timefield--default", "description": "Native time input styled to match the design system.", "name": "time-field", + "platforms": [ + "web" + ], "stories": [ { "id": "form-timefield--default", @@ -5079,6 +5930,9 @@ "defaultStoryId": "form-timepicker--default", "description": "Popover time selector built from hour and minute columns.", "name": "time-picker", + "platforms": [ + "web" + ], "stories": [ { "id": "form-timepicker--default", @@ -5096,6 +5950,9 @@ "defaultStoryId": "content-timeline--default", "description": "Vertical or horizontal timeline of sequential events with completed/active/upcoming statuses and connector lines.", "name": "timeline", + "platforms": [ + "web" + ], "stories": [ { "id": "content-timeline--default", @@ -5121,6 +5978,9 @@ "defaultStoryId": "canvas-timelinescrubber--default", "description": "Range slider for scrubbing through canvas state playback, with optional milestone ticks.", "name": "timeline-scrubber", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-timelinescrubber--default", @@ -5146,6 +6006,9 @@ "defaultStoryId": "content-tldrsection--default", "description": "Summary section for quick content overview.", "name": "tldr-section", + "platforms": [ + "web" + ], "stories": [ { "id": "content-tldrsection--default", @@ -5159,6 +6022,9 @@ "defaultStoryId": "overlay-toast--default", "description": "Temporary notification messages with action support.", "name": "toast", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-toast--default", @@ -5180,6 +6046,9 @@ "defaultStoryId": "core-toggle--default", "description": "Two-state toggle button.", "name": "toggle", + "platforms": [ + "web" + ], "stories": [ { "id": "core-toggle--default", @@ -5205,6 +6074,9 @@ "defaultStoryId": "core-togglegroup--default", "description": "Group of toggle buttons for single or multiple selection.", "name": "toggle-group", + "platforms": [ + "web" + ], "stories": [ { "id": "core-togglegroup--default", @@ -5218,6 +6090,9 @@ "defaultStoryId": "core-toolbar--default", "description": "Horizontal control group (role=toolbar) with arrow-key roving focus and separators.", "name": "toolbar", + "platforms": [ + "web" + ], "stories": [ { "id": "core-toolbar--default", @@ -5235,6 +6110,9 @@ "defaultStoryId": "overlay-tooltip--default", "description": "Informational popup displayed on hover or focus.", "name": "tooltip", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-tooltip--default", @@ -5248,6 +6126,9 @@ "defaultStoryId": "layout-topbar--default", "description": "Workspace header bar for titles, leading controls, centered navigation, and trailing actions.", "name": "top-bar", + "platforms": [ + "web" + ], "stories": [ { "id": "layout-topbar--default", @@ -5261,6 +6142,9 @@ "defaultStoryId": "learning-tour--default", "description": "Guided onboarding flow for introducing content or interface patterns.", "name": "tour", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-tour--default", @@ -5274,6 +6158,9 @@ "defaultStoryId": "billing-transactionlist--default", "description": "Chronological credit/debit history with locale-aware currency formatting and a pinned subscription row.", "name": "transaction-list", + "platforms": [ + "web" + ], "stories": [ { "id": "billing-transactionlist--default", @@ -5299,6 +6186,9 @@ "defaultStoryId": "data-display-treeview--default", "description": "Hierarchical tree component for nested data with controlled state, single/multi-select, and keyboard navigation.", "name": "tree-view", + "platforms": [ + "web" + ], "stories": [ { "id": "data-display-treeview--default", @@ -5324,6 +6214,9 @@ "defaultStoryId": "utility-truncatedtext--default", "description": "Single-line text that truncates with an ellipsis when it exceeds a configurable max width.", "name": "truncated-text", + "platforms": [ + "web" + ], "stories": [ { "id": "utility-truncatedtext--default", @@ -5337,6 +6230,9 @@ "defaultStoryId": "learning-tutorialcard--default", "description": "Card for displaying tutorial previews with metadata.", "name": "tutorial-card", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-tutorialcard--default", @@ -5350,6 +6246,9 @@ "defaultStoryId": "learning-tutorialcomplete--default", "description": "Completion screen displayed when a tutorial is finished.", "name": "tutorial-complete", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-tutorialcomplete--default", @@ -5367,6 +6266,9 @@ "defaultStoryId": "learning-tutorialfilters--default", "description": "Filter controls for browsing tutorials by category or difficulty.", "name": "tutorial-filters", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-tutorialfilters--default", @@ -5380,6 +6282,9 @@ "defaultStoryId": "learning-tutorialintrocontent--default", "description": "Introduction section for tutorial pages with overview and prerequisites.", "name": "tutorial-intro-content", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-tutorialintrocontent--default", @@ -5393,6 +6298,9 @@ "defaultStoryId": "learning-tutorialmdx--default", "description": "MDX renderer tailored for tutorial content with custom components.", "name": "tutorial-mdx", + "platforms": [ + "web" + ], "stories": [ { "id": "learning-tutorialmdx--default", @@ -5406,6 +6314,9 @@ "defaultStoryId": "effects-typewriter--default", "description": "Types text out character by character with a blinking cursor.", "name": "typewriter", + "platforms": [ + "web" + ], "stories": [ { "id": "effects-typewriter--default", @@ -5427,6 +6338,9 @@ "defaultStoryId": "core-typography--headings", "description": "Semantic text primitives: headings, paragraph, lead, muted, blockquote, inline code, and list.", "name": "typography", + "platforms": [ + "web" + ], "stories": [ { "id": "core-typography--headings", @@ -5452,6 +6366,9 @@ "defaultStoryId": "analytics-usagebreakdown--default", "description": "Ranked resource consumption list with relative share and trend cues.", "name": "usage-breakdown", + "platforms": [ + "web" + ], "stories": [ { "id": "analytics-usagebreakdown--default", @@ -5473,6 +6390,9 @@ "defaultStoryId": "content-videoembed--default", "description": "Responsive video embed for YouTube and other providers.", "name": "video-embed", + "platforms": [ + "web" + ], "stories": [ { "id": "content-videoembed--default", @@ -5486,6 +6406,9 @@ "defaultStoryId": "navigation-viewswitcher--default", "description": "URL param-based toggle between named views with pill/tab styling.", "name": "view-switcher", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-viewswitcher--default", @@ -5499,6 +6422,9 @@ "defaultStoryId": "canvas-viewportbookmarks--default", "description": "Saved-view list for the canvas — pinned spatial locations with optional active state.", "name": "viewport-bookmarks", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-viewportbookmarks--default", @@ -5524,6 +6450,9 @@ "defaultStoryId": "account-billing-walletcard--default", "description": "Credit balance display card for available, pending, and refresh details.", "name": "wallet-card", + "platforms": [ + "web" + ], "stories": [ { "id": "account-billing-walletcard--default", @@ -5541,6 +6470,9 @@ "defaultStoryId": "data-watchlist--default", "description": "Tracked-symbol list with price, change, and advancing/declining summary.", "name": "watchlist", + "platforms": [ + "web" + ], "stories": [ { "id": "data-watchlist--default", @@ -5562,6 +6494,9 @@ "defaultStoryId": "navigation-workspaceswitcher--default", "description": "Segmented workspace picker for switching between canvas views and operational contexts.", "name": "workspace-switcher", + "platforms": [ + "web" + ], "stories": [ { "id": "navigation-workspaceswitcher--default", @@ -5575,6 +6510,9 @@ "defaultStoryId": "canvas-worldbreadcrumbs--default", "description": "Spatial trail showing the canvas's current location in a hierarchy of worlds, groups, and runs.", "name": "world-breadcrumbs", + "platforms": [ + "web" + ], "stories": [ { "id": "canvas-worldbreadcrumbs--default", @@ -5600,6 +6538,9 @@ "defaultStoryId": "data-worldclockbar--default", "description": "Multi-timezone display for follow-the-sun teams and operational handoffs.", "name": "world-clock-bar", + "platforms": [ + "web" + ], "stories": [ { "id": "data-worldclockbar--default", @@ -5621,6 +6562,9 @@ "defaultStoryId": "overlay-zoomhud--default", "description": "Zoom controls with current percentage, increment buttons, and reset action for canvas views.", "name": "zoom-hud", + "platforms": [ + "web" + ], "stories": [ { "id": "overlay-zoomhud--default", diff --git a/apps/registry/lib/docs-pages.ts b/apps/registry/lib/docs-pages.ts index 57b9e570..d5e8fb0b 100644 --- a/apps/registry/lib/docs-pages.ts +++ b/apps/registry/lib/docs-pages.ts @@ -29,6 +29,12 @@ export const DOCS_PAGES: readonly DocsPage[] = [ slug: "registry", title: "Registry", }, + { + description: + "Build Expo apps with the experimental React Native renderer and shared VLLNT UI tokens.", + slug: "native", + title: "React Native", + }, { description: "Learn component anatomy, accessibility expectations, composition patterns, and test coverage.", diff --git a/apps/registry/lib/jsonld.test.ts b/apps/registry/lib/jsonld.test.ts index 723e088b..71c07b57 100644 --- a/apps/registry/lib/jsonld.test.ts +++ b/apps/registry/lib/jsonld.test.ts @@ -75,6 +75,7 @@ describe("softwareSourceCodeLd", () => { description: "A button.", locale: "fr", name: "button", + platforms: ["web"], title: "Button", }), ); @@ -88,6 +89,7 @@ describe("softwareSourceCodeLd", () => { description: "A button.", locale: "en", name: "button", + platforms: ["web"], title: "Button", }), ); @@ -95,4 +97,16 @@ describe("softwareSourceCodeLd", () => { expect(json).not.toContain("/fr"); expect(json).toMatch(/"url":"https:\/\/[^"]*\/components\/button"/); }); + + it("describes both renderer runtimes for a dual-platform component", () => { + const result = softwareSourceCodeLd({ + description: "A button.", + locale: "en", + name: "button", + platforms: ["web", "native"], + title: "Button", + }); + + expect(result.runtimePlatform).toEqual(["React", "React Native"]); + }); }); diff --git a/apps/registry/lib/jsonld.ts b/apps/registry/lib/jsonld.ts index 861eb567..854077fd 100644 --- a/apps/registry/lib/jsonld.ts +++ b/apps/registry/lib/jsonld.ts @@ -1,3 +1,5 @@ +import type { ComponentPlatform } from "@vllnt/ui-core"; + import type { Locale } from "@/i18n/routing"; import { canonical } from "@/lib/seo"; @@ -49,8 +51,13 @@ export function softwareSourceCodeLd(component: { readonly keywords?: readonly string[]; readonly locale: Locale; readonly name: string; + readonly platforms: readonly ComponentPlatform[]; readonly title: string; }): JsonLdNode { + const runtimes = component.platforms.map((platform) => + platform === "native" ? "React Native" : "React", + ); + return { "@context": "https://schema.org", "@type": "SoftwareSourceCode", @@ -63,7 +70,7 @@ export function softwareSourceCodeLd(component: { license: "https://opensource.org/license/mit", name: component.title, programmingLanguage: "TypeScript", - runtimePlatform: "React", + runtimePlatform: runtimes.length === 1 ? runtimes.join("") : runtimes, url: canonical(`/components/${component.name}`, component.locale), }; } diff --git a/apps/registry/lib/portable-contracts.test.ts b/apps/registry/lib/portable-contracts.test.ts new file mode 100644 index 00000000..42100426 --- /dev/null +++ b/apps/registry/lib/portable-contracts.test.ts @@ -0,0 +1,107 @@ +import { + type BadgeProps, + type ButtonProps, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, + type HeadingLevel, + type TextProps, +} from "@vllnt/ui"; +import type { + BadgeVariant, + ButtonSize, + ButtonVariant, + CardPart, + HeadingLevel as CoreHeadingLevel, + TextSize, + TextTone, + TextWeight, +} from "@vllnt/ui-core"; +import { describe, expect, it } from "vitest"; + +const badgeVariants = { + default: true, + destructive: true, + outline: true, + secondary: true, +} satisfies Record & + Record, true>; + +const buttonSizes = { + default: true, + icon: true, + lg: true, + sm: true, +} satisfies Record & + Record, true>; + +const buttonVariants = { + default: true, + destructive: true, + ghost: true, + link: true, + outline: true, + secondary: true, +} satisfies Record & + Record, true>; + +const cardParts: readonly CardPart[] = [ + "Card", + "CardContent", + "CardDescription", + "CardFooter", + "CardHeader", + "CardTitle", +]; +const webCardParts = [ + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +]; + +const headingLevels = [1, 2, 3, 4, 5, 6] satisfies readonly CoreHeadingLevel[]; +const webHeadingLevels: readonly HeadingLevel[] = headingLevels; + +const textSizes = { + base: true, + caption: true, + lead: true, + small: true, +} satisfies Record & + Record, true>; + +const textTones = { + default: true, + muted: true, +} satisfies Record & + Record, true>; + +const textWeights = { + medium: true, + normal: true, + semibold: true, +} satisfies Record & + Record, true>; + +describe("portable component contracts", () => { + it("remain structurally compatible with the public web renderer", () => { + expect([ + badgeVariants, + buttonSizes, + buttonVariants, + cardParts, + webCardParts, + headingLevels, + webHeadingLevels, + textSizes, + textTones, + textWeights, + ]).toHaveLength(10); + }); +}); diff --git a/apps/registry/lib/registry.test.ts b/apps/registry/lib/registry.test.ts new file mode 100644 index 00000000..30f64267 --- /dev/null +++ b/apps/registry/lib/registry.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; + +import { registryComponentSchema } from "./registry"; + +const baseComponent = { + files: [ + { path: "registry/default/button/button.tsx", type: "registry:component" }, + ], + name: "button", + platforms: ["web"] as const, + title: "Button", + type: "registry:component" as const, +}; + +describe("registry component platforms", () => { + it("accepts a web-only descriptor", () => { + expect(registryComponentSchema.parse(baseComponent).platforms).toEqual([ + "web", + ]); + }); + + it("accepts matching experimental native metadata and examples", () => { + const parsed = registryComponentSchema.parse({ + ...baseComponent, + examples: [ + { + code: 'import { Button } from "@vllnt/ui-native";', + framework: "react-native", + title: "Native button", + }, + ], + native: { + channel: "canary", + package: "@vllnt/ui-native", + parity: "full", + status: "experimental", + }, + platforms: ["web", "native"], + }); + + expect(parsed.native?.package).toBe("@vllnt/ui-native"); + expect(parsed.examples?.[0]?.framework).toBe("react-native"); + }); + + it.each([ + { ...baseComponent, platforms: [] }, + { ...baseComponent, platforms: ["web", "web"] }, + { ...baseComponent, platforms: ["desktop"] }, + { ...baseComponent, platforms: ["web", "native"] }, + { + ...baseComponent, + native: { + channel: "canary", + package: "@vllnt/ui-native", + parity: "full", + status: "experimental", + }, + }, + ])("rejects inconsistent platform metadata", (component) => { + expect(registryComponentSchema.safeParse(component).success).toBe(false); + }); +}); diff --git a/apps/registry/lib/registry.ts b/apps/registry/lib/registry.ts index 7b718dc2..c5e756d5 100644 --- a/apps/registry/lib/registry.ts +++ b/apps/registry/lib/registry.ts @@ -1,3 +1,4 @@ +import { componentPlatforms } from "@vllnt/ui-core"; import { z } from "zod"; import registryData from "@/registry.json"; @@ -27,6 +28,22 @@ export const componentCategorySchema = z.enum([ export type ComponentCategory = z.infer; +export const componentPlatformSchema = z.enum(componentPlatforms); + +const componentPlatformsSchema = z + .array(componentPlatformSchema) + .min(1) + .refine((platforms) => new Set(platforms).size === platforms.length, { + message: "Component platforms must be unique.", + }); + +const nativeRendererSchema = z.object({ + channel: z.literal("canary"), + package: z.literal("@vllnt/ui-native"), + parity: z.enum(["api-only", "full"]), + status: z.literal("experimental"), +}); + const stabilitySchema = z.enum([ "beta", "deprecated", @@ -55,7 +72,7 @@ const a11ySchema = z.object({ export const usageExampleSchema = z.object({ code: z.string(), description: z.string().optional(), - framework: z.enum(["next", "react"]).optional(), + framework: z.enum(["next", "react", "react-native"]).optional(), storyId: z.string().optional(), title: z.string(), }); @@ -71,22 +88,36 @@ const componentPropertyDefinitionSchema = z.object({ type: z.string(), }); -export const registryComponentSchema = z.object({ - a11y: a11ySchema.optional(), - category: componentCategorySchema.optional(), - dependencies: z.array(z.string()).optional(), - description: z.string().optional(), - examples: z.array(usageExampleSchema).optional(), - files: z.array(registryFileSchema), - name: z.string(), - props: z.array(componentPropertyDefinitionSchema).optional(), - registryDependencies: z.array(z.string()).optional(), - replacedBy: z.string().optional(), - stability: stabilitySchema.optional(), - title: z.string(), - type: z.literal("registry:component"), - version: z.string().optional(), -}); +export const registryComponentSchema = z + .object({ + a11y: a11ySchema.optional(), + category: componentCategorySchema.optional(), + dependencies: z.array(z.string()).optional(), + description: z.string().optional(), + examples: z.array(usageExampleSchema).optional(), + files: z.array(registryFileSchema), + name: z.string(), + native: nativeRendererSchema.optional(), + platforms: componentPlatformsSchema, + props: z.array(componentPropertyDefinitionSchema).optional(), + registryDependencies: z.array(z.string()).optional(), + replacedBy: z.string().optional(), + stability: stabilitySchema.optional(), + title: z.string(), + type: z.literal("registry:component"), + version: z.string().optional(), + }) + .superRefine((component, context) => { + const supportsNative = component.platforms.includes("native"); + if (supportsNative !== Boolean(component.native)) { + context.addIssue({ + code: "custom", + message: + 'A component must include native metadata exactly when platforms contains "native".', + path: ["native"], + }); + } + }); export type RegistryComponent = z.infer; @@ -102,3 +133,5 @@ export const registrySchema = z.object({ export type Registry = z.infer; export const registry: Registry = registrySchema.parse(registryData); + +export { type ComponentPlatform } from "@vllnt/ui-core"; diff --git a/apps/registry/messages/en.json b/apps/registry/messages/en.json index a12915e3..fdbb6880 100644 --- a/apps/registry/messages/en.json +++ b/apps/registry/messages/en.json @@ -9,6 +9,9 @@ "home": "Home", "locale": "Language", "philosophy": "Philosophy", + "platformNativeExperimental": "Native · Experimental", + "platforms": "Supported platforms", + "platformWeb": "Web", "readDocs": "Read the docs", "requestComponent": "Request a component" }, @@ -188,6 +191,9 @@ "dependencies": "Dependencies", "faq": "FAQ", "installation": "Installation", + "nativeInstallDescription": "Install the experimental React Native renderer from the canary channel. The stable web package and its API remain unchanged.", + "nativeInstallation": "React Native installation", + "nativeReadGuide": "Read the React Native guide", "preview": "Preview", "related": "Related components", "reportBug": "Report a bug", @@ -205,7 +211,12 @@ "ctaDescription": "Request a new component and we'll open a prefilled GitHub issue with the right labels and template.", "ctaTitle": "Don't see what you need?", "description": "Explore all {count} components available in the library.", + "noPlatformResults": "No components support this platform yet.", "noPreview": "No preview", + "platformAll": "All", + "platformFilterLabel": "Filter by platform", + "platformNative": "Native", + "platformWeb": "Web", "stories": "{count, plural, one {# story} other {# stories}}", "title": "Components" }, diff --git a/apps/registry/messages/fr.json b/apps/registry/messages/fr.json index 940d02ac..8d164441 100644 --- a/apps/registry/messages/fr.json +++ b/apps/registry/messages/fr.json @@ -9,6 +9,9 @@ "home": "Accueil", "locale": "Langue", "philosophy": "Philosophie", + "platformNativeExperimental": "Natif · Experimental", + "platforms": "Plateformes prises en charge", + "platformWeb": "Web", "readDocs": "Lire la documentation", "requestComponent": "Demander un composant" }, @@ -188,6 +191,9 @@ "dependencies": "Dependances", "faq": "FAQ", "installation": "Installation", + "nativeInstallDescription": "Installez le renderer React Native experimental depuis le canal canary. Le paquet web stable et son API restent inchanges.", + "nativeInstallation": "Installation React Native", + "nativeReadGuide": "Lire le guide React Native", "preview": "Apercu", "related": "Composants similaires", "reportBug": "Signaler un bug", @@ -205,7 +211,12 @@ "ctaDescription": "Demandez un nouveau composant et nous ouvrirons une issue GitHub pre-remplie avec les bons labels et le bon template.", "ctaTitle": "Vous ne trouvez pas ce dont vous avez besoin ?", "description": "Explorez les {count} composants disponibles dans la bibliotheque.", + "noPlatformResults": "Aucun composant ne prend encore en charge cette plateforme.", "noPreview": "Aucun apercu", + "platformAll": "Tous", + "platformFilterLabel": "Filtrer par plateforme", + "platformNative": "Natif", + "platformWeb": "Web", "stories": "{count, plural, one {# story} other {# stories}}", "title": "Composants" }, diff --git a/apps/registry/package.json b/apps/registry/package.json index d15c32d0..a88bb526 100644 --- a/apps/registry/package.json +++ b/apps/registry/package.json @@ -31,6 +31,7 @@ "@vercel/speed-insights": "^1.2.0", "@vllnt/next-llms": "canary", "@vllnt/ui": "workspace:*", + "@vllnt/ui-core": "workspace:*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "gray-matter": "^4.0.3", diff --git a/apps/registry/registry.json b/apps/registry/registry.json index 94635c55..13bd8917 100644 --- a/apps/registry/registry.json +++ b/apps/registry/registry.json @@ -20,7 +20,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "activity-heatmap", @@ -39,7 +42,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "activity-log", @@ -58,7 +64,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "agent-activity", @@ -78,7 +87,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-artifact", @@ -98,7 +110,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-chat-input", @@ -118,7 +133,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-message-bubble", @@ -138,7 +156,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-sidebar", @@ -158,7 +179,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-source-citation", @@ -178,7 +202,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-streaming-text", @@ -197,7 +224,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ai-tool-call-display", @@ -218,7 +248,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "alert", @@ -237,7 +270,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "alert-dialog", @@ -256,7 +292,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "alert-pulse", @@ -275,7 +314,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "anchor-port", @@ -294,7 +336,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "animated-beam", @@ -313,7 +358,10 @@ ], "category": "data-display", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "animated-grid-pattern", @@ -332,7 +380,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "animated-list", @@ -351,7 +402,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "animated-tabs", @@ -370,7 +424,10 @@ ], "category": "navigation", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "animated-testimonials", @@ -389,7 +446,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "animated-text", @@ -408,7 +468,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "animated-tooltip", @@ -427,7 +490,10 @@ ], "category": "overlay", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "annotation", @@ -446,7 +512,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "area-chart", @@ -465,7 +534,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "aspect-ratio", @@ -484,7 +556,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "auto-reload", @@ -503,7 +578,10 @@ ], "category": "billing", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "avatar", @@ -522,7 +600,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "avatar-group", @@ -541,7 +622,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "badge", @@ -560,7 +644,17 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web", + "native" + ], + "native": { + "channel": "canary", + "package": "@vllnt/ui-native", + "parity": "full", + "status": "experimental" + } }, { "name": "banner", @@ -582,7 +676,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "bar-chart", @@ -601,7 +698,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "bento-grid", @@ -620,7 +720,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "blog-card", @@ -639,7 +742,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "blur-reveal", @@ -658,7 +764,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "border-beam", @@ -677,7 +786,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "bottom-activity-strip", @@ -696,7 +808,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "bottom-bar", @@ -715,7 +830,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "breadcrumb", @@ -734,7 +852,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "button", @@ -816,7 +937,17 @@ "required": false, "description": "All native }\n />\n );\n}\n", "framework": "react" } + ], + "platforms": [ + "web" ] }, { @@ -1984,7 +2281,10 @@ ], "category": "educational", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "exercise", @@ -2003,7 +2303,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "expandable-cards", @@ -2022,7 +2325,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "faq", @@ -2041,7 +2347,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "field", @@ -2060,7 +2369,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "fieldset", @@ -2079,7 +2391,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "file-upload", @@ -2098,7 +2413,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "filter-bar", @@ -2117,7 +2435,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "flashcard", @@ -2136,7 +2457,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "floating-action-button", @@ -2155,7 +2479,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "floating-navbar", @@ -2174,7 +2501,10 @@ ], "category": "navigation", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "floating-toolbar", @@ -2193,7 +2523,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "flow-diagram", @@ -2212,7 +2545,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "follow-mode", @@ -2231,7 +2567,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "form", @@ -2250,7 +2589,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "gantt-chart", @@ -2269,7 +2611,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "gauge-chart", @@ -2288,7 +2633,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "geography-quiz-map", @@ -2307,7 +2655,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "glass-card", @@ -2326,7 +2677,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "glass-panel", @@ -2345,7 +2699,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "glass-progress", @@ -2364,7 +2721,10 @@ ], "category": "data-display", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "globe-3d", @@ -2383,7 +2743,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "grid", @@ -2402,7 +2765,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "group-hull", @@ -2421,7 +2787,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "handoff-beacon", @@ -2440,7 +2809,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "heading", @@ -2463,6 +2835,16 @@ "a11y": { "role": "heading", "notes": "Renders a native h1–h6 chosen by `level` for a correct document outline. `size` restyles the visual scale without changing the semantic rank, so the outline stays intact. No ARIA required." + }, + "platforms": [ + "web", + "native" + ], + "native": { + "channel": "canary", + "package": "@vllnt/ui-native", + "parity": "full", + "status": "experimental" } }, { @@ -2482,7 +2864,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "heat-overlay", @@ -2501,7 +2886,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "historic-timeline", @@ -2520,7 +2908,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "historical-figure-card", @@ -2540,7 +2931,10 @@ ], "category": "educational", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "horizontal-scroll-row", @@ -2560,7 +2954,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "hover-card", @@ -2579,7 +2976,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "infinite-plane", @@ -2598,7 +2998,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "inline-input", @@ -2617,7 +3020,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "input", @@ -2636,7 +3042,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "input-group", @@ -2655,8 +3064,11 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" - }, + "stability": "stable", + "platforms": [ + "web" + ] + }, { "name": "input-otp", "type": "registry:component", @@ -2674,7 +3086,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "interactive-timeline", @@ -2693,7 +3108,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "item", @@ -2712,7 +3130,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "jarvis-dock", @@ -2731,7 +3152,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "kbd", @@ -2751,7 +3175,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "key-concept", @@ -2770,7 +3197,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "keyboard-shortcuts-help", @@ -2789,7 +3219,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "knowledge-check", @@ -2809,7 +3242,10 @@ ], "category": "educational", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "label", @@ -2828,7 +3264,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "lang-provider", @@ -2847,7 +3286,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "learning-objectives", @@ -2866,7 +3308,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "left-rail", @@ -2885,7 +3330,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "line-chart", @@ -2904,7 +3352,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "link", @@ -2924,7 +3375,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "liquid-glass", @@ -2943,7 +3397,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "list-box", @@ -2962,7 +3419,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "live-cursor", @@ -2981,7 +3441,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "live-feed", @@ -3000,7 +3463,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "magnetic", @@ -3019,7 +3485,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "magnetic-button", @@ -3038,7 +3507,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "map-2d", @@ -3057,7 +3529,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "map-timeline", @@ -3076,7 +3551,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "market-treemap", @@ -3095,7 +3573,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "marquee", @@ -3114,7 +3595,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "mdx-content", @@ -3136,7 +3620,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "menubar", @@ -3155,7 +3642,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "meteors", @@ -3174,7 +3664,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "meter", @@ -3193,7 +3686,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "metric-cluster", @@ -3212,7 +3708,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "metric-gauge", @@ -3231,7 +3730,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "mini-map-panel", @@ -3250,7 +3752,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "model-comparison", @@ -3270,7 +3775,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "model-selector", @@ -3289,7 +3797,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "multi-select", @@ -3308,7 +3819,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "multi-select-lasso", @@ -3327,7 +3841,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "native-select", @@ -3346,7 +3863,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "navbar-saas", @@ -3365,7 +3885,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "navigation-menu", @@ -3384,7 +3907,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "newsletter-signup", @@ -3404,7 +3930,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "number-input", @@ -3423,7 +3952,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "number-ticker", @@ -3442,7 +3974,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "object-card", @@ -3461,7 +3996,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "object-handle", @@ -3480,7 +4018,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "object-inspector", @@ -3499,7 +4040,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "order-book", @@ -3518,7 +4062,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "overview-board", @@ -3537,7 +4084,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "pagination", @@ -3556,7 +4106,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "panel", @@ -3575,7 +4128,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "parallel-timeline", @@ -3594,7 +4150,10 @@ ], "category": "educational", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "particles", @@ -3613,7 +4172,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "password-input", @@ -3632,7 +4194,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "phone-input", @@ -3651,7 +4216,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "pie-chart", @@ -3670,7 +4238,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "plan-badge", @@ -3689,7 +4260,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "playback-ghost", @@ -3708,7 +4282,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "policy-delivery-panel", @@ -3727,7 +4304,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "popover", @@ -3746,7 +4326,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "presence-stack", @@ -3765,7 +4348,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "presence-sync-indicator", @@ -3784,7 +4370,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "pricing-table", @@ -3804,7 +4393,10 @@ ], "category": "billing", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "primary-source-viewer", @@ -3823,7 +4415,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "pro-tip", @@ -3842,7 +4437,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "profile-section", @@ -3861,7 +4459,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "progress-bar", @@ -3880,7 +4481,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "progress-card", @@ -3899,7 +4503,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "progress-tracker", @@ -3918,7 +4525,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "progressive-blur", @@ -3937,7 +4547,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "prompt-input", @@ -3957,7 +4570,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "prompt-templates", @@ -3977,7 +4593,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "property-section", @@ -3996,7 +4615,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "prose", @@ -4018,7 +4640,10 @@ "stability": "stable", "a11y": { "notes": "Styling-only wrapper that preserves the semantics of the HTML you nest (headings, lists, quotes, code), so document structure and reading order come from your content." - } + }, + "platforms": [ + "web" + ] }, { "name": "qr-code", @@ -4038,7 +4663,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "quiz", @@ -4057,7 +4685,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "radar-chart", @@ -4076,7 +4707,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "radio-group", @@ -4095,7 +4729,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "range-calendar", @@ -4114,7 +4751,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "rating", @@ -4133,7 +4773,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "reasoning", @@ -4153,7 +4796,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "relationship-inspector", @@ -4172,7 +4818,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "resizable", @@ -4191,7 +4840,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "reveal-text", @@ -4210,7 +4862,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "right-dock", @@ -4229,7 +4884,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "role-badge", @@ -4248,7 +4906,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "route-map", @@ -4267,7 +4928,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "routing-assignment-panel", @@ -4286,7 +4950,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "run-timeline", @@ -4305,7 +4972,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "runtime-overview-panel", @@ -4324,7 +4994,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "sankey-chart", @@ -4343,7 +5016,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "scope-selector", @@ -4362,7 +5038,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "scramble-text", @@ -4381,7 +5060,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "scroll-area", @@ -4400,7 +5082,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "scroll-progress", @@ -4419,7 +5104,10 @@ ], "category": "data-display", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "search-bar", @@ -4438,7 +5126,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "search-dialog", @@ -4457,7 +5148,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "search-field", @@ -4476,7 +5170,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "segmented-control", @@ -4495,7 +5192,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "select", @@ -4514,7 +5214,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "selection-halo", @@ -4533,7 +5236,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "selection-presence", @@ -4552,7 +5258,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "separator", @@ -4571,7 +5280,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "severity-badge", @@ -4590,7 +5302,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "share-dialog", @@ -4609,7 +5324,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "share-section", @@ -4628,7 +5346,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "sheet", @@ -4647,7 +5368,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "shimmer-button", @@ -4666,7 +5390,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "shimmer-text", @@ -4685,7 +5412,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "shine-border", @@ -4704,7 +5434,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "shiny-button", @@ -4723,7 +5456,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "sidebar", @@ -4742,7 +5478,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "sidebar-provider", @@ -4761,7 +5500,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "sidebar-toggle", @@ -4781,7 +5523,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "skeleton", @@ -4800,7 +5545,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "slider", @@ -4819,7 +5567,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "slideshow", @@ -4838,7 +5589,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "snap-guides", @@ -4857,7 +5611,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "sparkles", @@ -4876,7 +5633,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "sparkline-grid", @@ -4895,7 +5655,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "spinner", @@ -4914,7 +5677,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "spinning-text", @@ -4933,7 +5699,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "spotlight-card", @@ -4952,7 +5721,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "stat-card", @@ -4971,7 +5743,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "state-badge-overlay", @@ -4990,7 +5765,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "status-board", @@ -5009,7 +5787,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "status-indicator", @@ -5028,7 +5809,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "step-by-step", @@ -5047,7 +5831,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "step-navigation", @@ -5066,7 +5853,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "stepper", @@ -5085,7 +5875,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "sticky-metric", @@ -5104,7 +5897,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "story-map", @@ -5123,7 +5919,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "subscription-card", @@ -5142,7 +5941,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "switch", @@ -5161,7 +5963,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "table", @@ -5180,7 +5985,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "table-of-contents", @@ -5199,7 +6007,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "table-of-contents-panel", @@ -5218,7 +6029,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tabs", @@ -5237,7 +6051,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tag-group", @@ -5256,7 +6073,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tags-input", @@ -5275,7 +6095,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "terminal", @@ -5294,7 +6117,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "text", @@ -5317,6 +6143,16 @@ "stability": "stable", "a11y": { "notes": "Renders a semantic element (p, span, div, or label) chosen by `as`, forwards `ref`, and inherits the design-system foreground contrast tokens. Use `tone=\"muted\"` on the default `background` — on `muted`/`card` surfaces the muted foreground can fall below WCAG AA. For a real form label use the Label component, which wires the htmlFor association." + }, + "platforms": [ + "web", + "native" + ], + "native": { + "channel": "canary", + "package": "@vllnt/ui-native", + "parity": "full", + "status": "experimental" } }, { @@ -5336,7 +6172,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "text-field", @@ -5355,7 +6194,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "text-reveal", @@ -5374,7 +6216,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "text-shimmer", @@ -5393,7 +6238,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "textarea", @@ -5412,7 +6260,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "theme-preset-provider", @@ -5431,7 +6282,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "theme-provider", @@ -5450,7 +6304,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "theme-switcher", @@ -5469,7 +6326,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "theme-toggle", @@ -5488,7 +6348,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "thinking-block", @@ -5508,7 +6371,10 @@ ], "category": "ai", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "thread-bubble", @@ -5527,7 +6393,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "threshold-ring", @@ -5546,7 +6415,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "ticker-tape", @@ -5565,7 +6437,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tilt-card", @@ -5584,7 +6459,10 @@ ], "category": "utility", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "time-field", @@ -5603,7 +6481,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "time-picker", @@ -5622,7 +6503,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "timeline", @@ -5642,7 +6526,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "timeline-scrubber", @@ -5661,7 +6548,10 @@ ], "category": "form", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tldr-section", @@ -5680,7 +6570,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "toast", @@ -5699,7 +6592,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "toggle", @@ -5718,7 +6614,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "toggle-group", @@ -5737,7 +6636,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "toolbar", @@ -5756,7 +6658,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tooltip", @@ -5775,7 +6680,10 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "top-bar", @@ -5794,7 +6702,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tour", @@ -5813,7 +6724,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "transaction-list", @@ -5832,7 +6746,10 @@ ], "category": "billing", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tree-view", @@ -5851,7 +6768,10 @@ ], "category": "data-display", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "truncated-text", @@ -5870,7 +6790,10 @@ ], "category": "utility", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tutorial-card", @@ -5889,7 +6812,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tutorial-complete", @@ -5908,7 +6834,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tutorial-filters", @@ -5927,7 +6856,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tutorial-intro-content", @@ -5946,7 +6878,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "tutorial-mdx", @@ -5965,7 +6900,10 @@ ], "category": "learning", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "typewriter", @@ -5984,7 +6922,10 @@ ], "category": "content", "stability": "stable", - "version": "0.3.0" + "version": "0.3.0", + "platforms": [ + "web" + ] }, { "name": "typography", @@ -6003,7 +6944,10 @@ ], "category": "core", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "usage-breakdown", @@ -6022,7 +6966,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "video-embed", @@ -6041,7 +6988,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "view-switcher", @@ -6060,7 +7010,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "viewport-bookmarks", @@ -6079,7 +7032,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "wallet-card", @@ -6098,7 +7054,10 @@ ], "category": "content", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "watchlist", @@ -6117,7 +7076,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "workspace-switcher", @@ -6136,7 +7098,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "world-breadcrumbs", @@ -6155,7 +7120,10 @@ ], "category": "navigation", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "world-clock-bar", @@ -6174,7 +7142,10 @@ ], "category": "data", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] }, { "name": "zoom-hud", @@ -6194,9 +7165,12 @@ ], "category": "overlay", "version": "0.3.0", - "stability": "stable" + "stability": "stable", + "platforms": [ + "web" + ] } ], "version": "0.3.0", - "generatedAt": "2026-07-13T15:22:06.484Z" + "generatedAt": "2026-09-03T18:10:12.887Z" } diff --git a/apps/registry/registry.ts b/apps/registry/registry.ts index ef24a80c..5d21ee18 100644 --- a/apps/registry/registry.ts +++ b/apps/registry/registry.ts @@ -1 +1 @@ -export { default as registry } from "./registry.json"; +export { registry as default, registry } from "./lib/registry"; diff --git a/apps/registry/scripts/check-registry-integrity.ts b/apps/registry/scripts/check-registry-integrity.ts index ed81ddeb..d86300d6 100644 --- a/apps/registry/scripts/check-registry-integrity.ts +++ b/apps/registry/scripts/check-registry-integrity.ts @@ -28,6 +28,7 @@ const scriptDir = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(scriptDir, "../../.."); const componentsRoot = join(repoRoot, "packages/ui/src/components"); const registryJsonPath = join(repoRoot, "apps/registry/registry.json"); +const nativeRegistryPath = join(repoRoot, "packages/ui-native/registry.json"); /** * Components that legitimately have a story + test but are NOT registry items. @@ -41,15 +42,28 @@ const EXCLUDED = new Set([ ]); type RegistryItem = { + dependencies?: string[]; name: string; - version?: string; + native?: { + channel?: string; + package?: string; + parity?: string; + status?: string; + }; + platforms?: string[]; stability?: string; - dependencies?: string[]; + version?: string; }; type Registry = { items: RegistryItem[] }; +type NativeRegistry = { + components: { name: string; parity: "api-only" | "full" }[]; +}; const registry = JSON.parse(readFileSync(registryJsonPath, "utf8")) as Registry; +const nativeRegistry = JSON.parse( + readFileSync(nativeRegistryPath, "utf8"), +) as NativeRegistry; const itemNames = new Set(registry.items.map((item) => item.name)); const errors: string[] = []; @@ -76,6 +90,34 @@ for (const item of registry.items) { if (!item.stability) { errors.push(`Item "${item.name}" is missing "stability".`); } + const platforms = item.platforms ?? []; + const platformSet = new Set(platforms); + if (platforms.length === 0) { + errors.push(`Item "${item.name}" is missing "platforms".`); + } + if (platformSet.size !== platforms.length) { + errors.push(`Item "${item.name}" has duplicate platforms.`); + } + if (platforms.some((platform) => platform !== "web" && platform !== "native")) { + errors.push(`Item "${item.name}" has an unsupported platform.`); + } + if (platforms[0] !== "web") { + errors.push(`Item "${item.name}" must list "web" first.`); + } + if (platformSet.has("native") !== Boolean(item.native)) { + errors.push( + `Item "${item.name}" must include native metadata exactly when native is supported.`, + ); + } + if ( + item.native && + (item.native.package !== "@vllnt/ui-native" || + item.native.channel !== "canary" || + item.native.status !== "experimental" || + !["api-only", "full"].includes(item.native.parity ?? "")) + ) { + errors.push(`Item "${item.name}" has invalid native renderer metadata.`); + } const uiDep = (item.dependencies ?? []).find((dep) => dep.startsWith("@vllnt/ui@"), ); @@ -93,6 +135,23 @@ for (const item of registry.items) { } } +const nativeManifest = new Map( + nativeRegistry.components.map((component) => [component.name, component.parity]), +); +const nativeItems = registry.items.filter((item) => + item.platforms?.includes("native"), +); +for (const item of nativeItems) { + if (nativeManifest.get(item.name) !== item.native?.parity) { + errors.push(`Item "${item.name}" has drifted from packages/ui-native/registry.json.`); + } +} +for (const name of nativeManifest.keys()) { + if (!nativeItems.some((item) => item.name === name)) { + errors.push(`Native manifest component "${name}" is absent from registry metadata.`); + } +} + if (depVersions.size > 1) { errors.push( `Inconsistent @vllnt/ui dependency versions across items: ${[...depVersions].join(", ")}.`, diff --git a/apps/registry/scripts/generate-component-metadata.ts b/apps/registry/scripts/generate-component-metadata.ts index 97bb2300..f3973ad9 100644 --- a/apps/registry/scripts/generate-component-metadata.ts +++ b/apps/registry/scripts/generate-component-metadata.ts @@ -33,6 +33,7 @@ type RegistryItem = { description?: string; files: { path: string; type: string }[]; name: string; + platforms: ("native" | "web")[]; title?: string; type: string; }; @@ -51,6 +52,7 @@ type ComponentMetadata = { defaultStoryId: string; description: string; name: string; + platforms: ("native" | "web")[]; stories: StoryEntry[]; title: string; }; @@ -257,6 +259,7 @@ for (const item of registry.items) { defaultStoryId: entries.defaultStoryId, description: item.description ?? "", name: item.name, + platforms: item.platforms, stories: entries.stories, title: item.title ?? item.name, }; diff --git a/apps/registry/scripts/inline-component-source.ts b/apps/registry/scripts/inline-component-source.ts index 5cc93884..ba5ded2c 100644 --- a/apps/registry/scripts/inline-component-source.ts +++ b/apps/registry/scripts/inline-component-source.ts @@ -39,6 +39,7 @@ import { fileURLToPath } from "node:url"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(scriptDir, "../../.."); const registryJsonPath = join(repoRoot, "apps/registry/registry.json"); +const nativeRegistryPath = join(repoRoot, "packages/ui-native/registry.json"); const componentsRoot = join(repoRoot, "packages/ui/src/components"); const shimsRoot = join(repoRoot, "apps/registry/registry/default"); @@ -57,6 +58,22 @@ type RegistryFile = { }; type Stability = "stable" | "beta" | "experimental" | "deprecated"; +type ComponentPlatform = "native" | "web"; +type NativeParity = "api-only" | "full"; + +type NativeRenderer = { + channel: "canary"; + package: "@vllnt/ui-native"; + parity: NativeParity; + status: "experimental"; +}; + +type NativeRegistry = { + channel: "canary"; + components: { name: string; parity: NativeParity }[]; + package: "@vllnt/ui-native"; + status: "experimental"; +}; type A11yKeyboardBinding = { keys: string; @@ -75,7 +92,7 @@ type UsageExample = { title: string; description?: string; code: string; - framework?: "react" | "next"; + framework?: "next" | "react" | "react-native"; storyId?: string; }; @@ -102,6 +119,8 @@ type RegistryItem = { examples?: UsageExample[]; files: RegistryFile[]; name: string; + native?: NativeRenderer; + platforms: ComponentPlatform[]; props?: PropDefinition[]; registryDependencies?: string[]; replacedBy?: string; @@ -208,6 +227,12 @@ const rewriteImports = (source: string): string => { }; const registry = JSON.parse(readFileSync(registryJsonPath, "utf8")) as Registry; +const nativeRegistry = JSON.parse( + readFileSync(nativeRegistryPath, "utf8"), +) as NativeRegistry; +const nativeComponents = new Map( + nativeRegistry.components.map((component) => [component.name, component]), +); let processed = 0; let skipped = 0; @@ -218,6 +243,19 @@ for (const item of registry.items) { continue; } + const nativeComponent = nativeComponents.get(item.name); + item.platforms = nativeComponent ? ["web", "native"] : ["web"]; + if (nativeComponent) { + item.native = { + channel: nativeRegistry.channel, + package: nativeRegistry.package, + parity: nativeComponent.parity, + status: nativeRegistry.status, + }; + } else { + delete item.native; + } + const sourcePath = join(componentsRoot, item.name, `${item.name}.tsx`); if (!existsSync(sourcePath)) { // No canonical `/.tsx` to inline (e.g. bar/line/area-chart are @@ -315,12 +353,29 @@ for (const name of RESERVED_REGISTRY_NAMES) { } } +const registryNames = new Set(registry.items.map((item) => item.name)); +const missingNativeComponents = nativeRegistry.components.filter( + (component) => !registryNames.has(component.name), +); +if (missingNativeComponents.length > 0) { + console.error( + `Native components missing from the web registry: ${missingNativeComponents + .map((component) => component.name) + .join(", ")}`, + ); + process.exitCode = 1; +} + // Sort items alphabetically for deterministic output registry.items.sort((a, b) => a.name.localeCompare(b.name)); -// Stamp top-level version + generatedAt so agents can detect schema/library changes. +// Refresh generatedAt only when the published registry version changes; normal +// deterministic rebuilds must not dirty the tree. +if (registry.version !== PUBLISHED_VERSION) { + registry.generatedAt = new Date().toISOString(); +} registry.version = PUBLISHED_VERSION; -registry.generatedAt = new Date().toISOString(); +registry.generatedAt ??= new Date().toISOString(); // Validate: any deprecated component must declare replacedBy. const deprecatedWithoutReplacement = registry.items.filter( diff --git a/apps/registry/scripts/stamp-registry-metadata.ts b/apps/registry/scripts/stamp-registry-metadata.ts index e04ea959..d15809d1 100644 --- a/apps/registry/scripts/stamp-registry-metadata.ts +++ b/apps/registry/scripts/stamp-registry-metadata.ts @@ -21,6 +21,14 @@ const registryJsonPath = join(repoRoot, "apps/registry/registry.json"); const publicRDir = join(repoRoot, "apps/registry/public/r"); type Stability = "stable" | "beta" | "experimental" | "deprecated"; +type ComponentPlatform = "native" | "web"; + +type NativeRenderer = { + channel: "canary"; + package: "@vllnt/ui-native"; + parity: "api-only" | "full"; + status: "experimental"; +}; type A11yKeyboardBinding = { keys: string; @@ -39,7 +47,7 @@ type UsageExample = { title: string; description?: string; code: string; - framework?: "react" | "next"; + framework?: "next" | "react" | "react-native"; storyId?: string; }; @@ -56,6 +64,8 @@ type RegistryItem = { a11y?: A11ySchema; examples?: UsageExample[]; name: string; + native?: NativeRenderer; + platforms: ComponentPlatform[]; props?: PropDefinition[]; version?: string; stability?: Stability; @@ -82,6 +92,12 @@ for (const item of registry.items) { data.version = item.version; data.stability = item.stability; + data.platforms = item.platforms; + if (item.native) { + data.native = item.native; + } else { + delete data.native; + } if (item.replacedBy) { data.replacedBy = item.replacedBy; } else { @@ -110,10 +126,25 @@ for (const item of registry.items) { // Patch the public registry index too — agents read /r/registry.json. const indexPath = join(publicRDir, "registry.json"); if (existsSync(indexPath)) { - const index = JSON.parse(readFileSync(indexPath, "utf8")) as Record< - string, - unknown - >; + const index = JSON.parse(readFileSync(indexPath, "utf8")) as { + generatedAt?: string; + items?: Record[]; + version?: string; + }; + const metadataByName = new Map( + registry.items.map((item) => [item.name, item]), + ); + for (const indexItem of index.items ?? []) { + const name = typeof indexItem.name === "string" ? indexItem.name : ""; + const metadata = metadataByName.get(name); + if (!metadata) continue; + indexItem.platforms = metadata.platforms; + if (metadata.native) { + indexItem.native = metadata.native; + } else { + delete indexItem.native; + } + } index.version = registry.version; index.generatedAt = registry.generatedAt; writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}\n`); diff --git a/apps/registry/types/registry.ts b/apps/registry/types/registry.ts index 345a094f..596a95f9 100644 --- a/apps/registry/types/registry.ts +++ b/apps/registry/types/registry.ts @@ -5,6 +5,7 @@ */ export type { ComponentCategory, + ComponentPlatform, Registry, RegistryComponent, UsageExample, diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 33876b5f..15f8dff0 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,93 +2,111 @@ ## Monorepo layout -``` +```text vllnt/ui/ ├── packages/ -│ └── ui/ # @vllnt/ui — the shipped library -│ ├── src/ -│ │ ├── components/ # 309 component folders -│ │ ├── hooks/ # shared hooks (useDebounce, etc.) -│ │ ├── lib/ # utilities (cn, registry helpers) -│ │ └── index.ts # barrel — the public surface -│ ├── dist/ # tsup output (published) -│ ├── styles.css -│ └── themes/ +│ ├── design/ # authored tokens + portable contracts + generator +│ ├── ui-core/ # @vllnt/ui-core — generated, platform-neutral data +│ ├── ui/ # @vllnt/ui — stable React DOM renderer +│ └── ui-native/ # @vllnt/ui-native — experimental RN renderer ├── apps/ -│ └── registry/ # Next.js site at ui.vllnt.com -│ ├── app/ # app-router routes (components/[slug], etc.) -│ ├── content/ # MDX pages -│ ├── lib/ # registry + OG utilities -│ └── registry.ts # shadcn-compatible registry feed -├── .github/workflows/ # ci.yml, publish.yml, storybook.yml -├── specs/ # feature specs (active + shipped) -└── docs/ # contributor docs (this folder) +│ ├── registry/ # Next.js docs + platform-aware registry +│ └── native-catalog/ # private Expo integration consumer +├── .github/workflows/ +├── specs/ +└── docs/ ``` ## Package boundaries -| Package | Purpose | Published | -|---------|---------|-----------| -| `@vllnt/ui` | Component library | Yes, public npm | -| `@vllnt/ui-registry` (registry app) | Docs + shadcn registry | No, deployed to `ui.vllnt.com` | +| Package | Purpose | Release policy | +|---------|---------|----------------| +| `@vllnt/ui` | React DOM components using Radix UI, Tailwind CSS, and CVA | Public `latest` + canary | +| `@vllnt/ui-core` | Framework-free tokens, native theme values, and portable option contracts | Experimental canary only | +| `@vllnt/ui-native` | React Native components using native primitives and `StyleSheet` | Experimental canary only | +| `@vllnt/ui-registry` | Docs, shadcn feed, search, and MCP | Private; deployed | +| `@vllnt/ui-native-catalog` | Expo integration and Metro bundle proof | Private; CI only | -External shared configs consumed as dev deps from npm: +`@vllnt/ui` does not depend on the experimental packages. Its exports, CSS entry points, DOM behavior, and stable release path remain unchanged. The native renderer depends on `@vllnt/ui-core`, never on the web renderer. The registry is the first private consumer of core metadata. -- `@vllnt/eslint-config` — ESLint 9 flat config -- `@vllnt/typescript` — shared `tsconfig` bases +External contributor tooling still comes from `@vllnt/eslint-config` and `@vllnt/typescript`. The web renderer uses React 19, Radix UI, Tailwind CSS, and CVA; the native renderer uses React 19 and React Native primitives. `tsup` builds libraries, Next.js builds the registry, and Expo/Metro validates the native integration. -## Tech stack +## Shared foundations -- **Runtime:** React 19, Radix UI primitives, Tailwind CSS 3, CVA, tailwind-merge. -- **Build:** `tsup` (library), Next.js (registry app). -- **Test:** Vitest (unit, `jsdom`), Playwright CT (visual, real Chromium), Storybook (interactive + test-runner smoke). -- **Lint:** ESLint 9 flat config. TypeScript strict. -- **Workspace:** pnpm workspaces + Turborepo. +`packages/design/tokens.json` is the authored token source. `packages/design/component-contracts.json` contains only portable semantic options such as Button variants, Text scales, and Heading levels. It deliberately excludes renderer details such as DOM attributes, `className`, `onClick`, React Native `style`, and `onPress`. -## Component module layout +The token generator writes committed artifacts for deterministic builds: -Every component is a self-contained folder: +- Existing `packages/ui/themes/default.css` and the token region of `packages/ui/styles.css`. +- `@vllnt/ui-core` TypeScript and JSON exports. +- React Native-compatible sRGB colors and point-based spacing, radius, type, and motion values. -``` -src/components/{name}/ - {name}.tsx # implementation — forwardRef + cn + CVA + Radix (if applicable) - {name}.test.tsx # Vitest unit tests - {name}.visual.tsx # Playwright CT story (real browser) - {name}.mdx # registry / docs content - index.ts # barrel export from the folder +```bash +pnpm tokens:generate +pnpm tokens:check ``` -The root `src/index.ts` re-exports components for consumers. +CI runs the read-only drift check. A token change is incomplete when generated web and core outputs disagree. -## Build graph +## Renderer model -``` -@vllnt/ui build ─▶ dist/ (tsup, preserves "use client") - └▶ styles.css (copied) - └▶ themes/ (copied) +### Web -registry app build ─▶ .next/ (consumes @vllnt/ui via workspace link in dev, - via tsup dist in production CI) -``` +`@vllnt/ui` targets React 19. Components render semantic DOM and Radix primitives, use Tailwind/CVA recipes, and accept refs as normal React 19 props. Existing package and CSS subpaths remain the supported contract. + +Web components remain self-contained under `packages/ui/src/components/{name}` with implementation, unit test, visual fixture, MDX documentation, and barrel export files as applicable. The root `src/index.ts` remains the public barrel. `pnpm check:circular` runs Madge against this graph. + +### React Native -`pnpm check:circular` runs `madge` to catch circular imports under `packages/ui/src`. +`@vllnt/ui-native` targets React 19 and React Native 0.81 or newer. The pilot includes Button, Text, Heading, Badge, and the Card compound family. Components consume the generated theme through `ThemeProvider`, expose React Native props, meet native touch-target and accessibility requirements, and have no DOM, Radix, Tailwind, or NativeWind dependency. + +NativeWind and `@rn-primitives` are intentionally absent from the foundational pilot. This avoids mandatory consumer Babel configuration and unnecessary runtime dependencies. Complex interaction families can add narrowly scoped adapters after real-device validation proves the need. ## Theming -All color values are **OKLCH channel** CSS variables (`L C H`) on `:root` and `.dark` in `styles.css` / `themes/default.css`; spacing and radius are length variables. Downstream apps override variables without patching components. The Tailwind preset (`@vllnt/ui/tailwind-preset`) maps variables to Tailwind tokens as `oklch(var(--name) / )` so utility classes (including opacity modifiers) stay in sync with theme overrides. +Web colors remain OKLCH channel CSS variables on `:root` and `.dark`; spacing, radius, and typography also remain CSS variables. The Tailwind preset and runtime preset themes continue to consume those variables, so downstream overrides do not require component patches. + +Core generation converts the authored OKLCH values to clipped sRGB hex for React Native and converts rem-based dimensions to numeric points. It applies two documented native accessibility adjustments: a near-black dark background instead of banned pure black, and a darker light destructive surface so small labels meet 4.5:1 contrast. Native light/dark themes are immutable data selected by `ThemeProvider`; system mode follows `useColorScheme`. These are deterministic renderer conversions, not a second authored token source. + +## Build graph -Beyond light/dark, a runtime **preset** layer (`themes/presets.css`) applies named themes via `data-theme` on the document root, switchable with `ThemeSwitcher` / `useThemePreset`. The registry app's `/themes` editor lets users author a custom OKLCH theme and export it as a CSS block, a `npx shadcn add` command (served by the `/r/themes` route handler), or design tokens. +```text +packages/design ──drift check──▶ generated web/core artifacts + │ +@vllnt/ui ────────────────────────────┤──▶ registry app + │ +@vllnt/ui-core ──▶ @vllnt/ui-native ─┴──▶ Expo native catalog +``` -## CI pipelines +Turborepo orders package builds through workspace dependencies. The native CI job additionally runs lint, strict type checks, unit/contract tests, renderer boundary checks, packed-artifact resolution, Expo Doctor, and Android/iOS Metro exports. + +| Workflow | Responsibility | +|----------|----------------| +| `ci.yml` | Existing workspace gates plus an isolated native package/Expo job | +| `publish.yml` | Existing `@vllnt/ui` canary and stable releases | +| `native-canary.yml` | Synchronized core/native canaries after native quality gates | +| `storybook.yml` | Existing web Storybook build and deployment | + +## Platform-aware registry + +`apps/registry/registry.json` remains the canonical shadcn-compatible index. Generation adds a required `platforms` array to every item. Native-capable entries also carry: + +```json +{ + "platforms": ["web", "native"], + "native": { + "package": "@vllnt/ui-native", + "channel": "canary", + "status": "experimental", + "parity": "full" + } +} +``` -| Workflow | Triggers | Jobs | -|----------|----------|------| -| `ci.yml` | push to `main`, PRs | install → lint → typecheck → test → build | -| `publish.yml` | push to `main`, manual dispatch | quality gates → canary (push) OR release (dispatch) | -| `storybook.yml` | push to `main`, PRs | build Storybook + optional deploy | +`shadcn build` strips extension fields, so `stamp-registry-metadata.ts` restores them in generated public descriptors. The website exposes platform badges and filtering; `/llms.txt`, `/llms-full.txt`, JSON routes, search, JSON-LD, and MCP return the same availability contract. -`publish.yml` uses `npx --yes npm@latest publish` so OIDC trusted publishing survives runner-bundled npm (which is older than 11.5.1). +Web installation remains shadcn-based. Native installation is package-based during the pilot. Registry metadata is discovery information, not a claim that the web shim runs on React Native. -## Registry feed +## Release boundaries -The shadcn-compatible registry at `https://ui.vllnt.com/r/{component}.json` is generated from `apps/registry/registry.ts` and served by the Next.js app. Each component resolves to a JSON payload consumable by `shadcn add`. +The existing `publish.yml` remains exclusively responsible for `@vllnt/ui`, including stable releases. `native-canary.yml` has no manual dispatch and cannot publish `latest`, create Git tags, or create GitHub releases. It publishes synchronized core/native canary versions in dependency order and verifies that neither `latest` tag moves. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index adb1418f..c30362a1 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -41,7 +41,7 @@ CI will: - Read the version from `packages/ui/package.json`. **Fails fast** if a matching `v{x.y.z}` tag already exists — catches dispatches against stale main. - Read the `CHANGELOG.md` section for the package version and use it as the GitHub Release notes. - Push an annotated tag `v{x.y.z}` (tags are not blocked by branch protection; `GITHUB_TOKEN` is sufficient). -- `pnpm pack` and `npx --yes npm@latest publish --tag latest --provenance --access public`. OIDC trusted publishing signs the provenance attestation. +- `pnpm pack` and `npx --yes npm@11.18.0 publish --tag latest --provenance --access public`. The pinned npm version avoids known provenance regressions while OIDC trusted publishing signs the attestation. - Create the GitHub Release for the new tag. ### 3. Point the registry at the published version (post-publish) @@ -53,6 +53,29 @@ Once `@vllnt/ui@{x.y.z}` is live on npm `latest`, open a small follow-up PR that The `registry:check` and `registry:integrity` CI guards confirm the regenerated registry is in sync and pins a real (non-prerelease) published version. Until this lands, `npx shadcn add` keeps resolving to the previous published version — harmless, just one release behind. +## Experimental native canaries + +`@vllnt/ui-core` and `@vllnt/ui-native` have a separate safety boundary in `.github/workflows/native-canary.yml`: + +- A push to `main` that changes native/core/token surfaces runs `pnpm ci:native`. +- Both packages receive the same `0.1.0-canary..sha` version. +- Core publishes first under a run-scoped staging tag; native publishes only after that exact core version is visible. +- Reruns skip immutable versions already present and reuse the run-scoped tag, allowing recovery from a partial pair. +- The workflow verifies packed names, versions, the rewritten core dependency, and absence of `workspace:` protocols. +- Only after both versions are visible does the workflow promote both `canary` tags. Ordinary failures restore the prior pair and clean up staging tags. +- Publication never targets `latest`; fail-closed registry reads verify that neither `latest` tag moves. +- No workflow dispatch, Git tag, GitHub Release, or stable publication path exists. + +Native publication is fail-closed behind the repository variable `NATIVE_CANARY_PUBLISH_ENABLED`. Before setting it to `true`, reserve both package names on npm, configure trusted-publisher entries, and create a protected GitHub environment named `npm-native-canary`. Until that setup is complete, the workflow still runs native quality gates but skips publication. + +Consume the pilot explicitly: + +```bash +pnpm add @vllnt/ui-native@canary +``` + +A stable native channel requires a separate PR that defines versioning, migration, device-validation, and rollback policy. It must not be added to the web package's release matrix. + ## Versioning policy - [SemVer](https://semver.org). Track API compatibility, not surface area — adding 50 new components is a **minor** bump if no existing exports break. diff --git a/doctor.config.json b/doctor.config.json index 4132bb6a..77d83f7f 100644 --- a/doctor.config.json +++ b/doctor.config.json @@ -1,6 +1,7 @@ { "$schema": "https://react.doctor/schema/config.json", - "failOn": "error", + "blocking": "error", + "rawTextWrapperComponents": ["Badge", "Button"], "rules": { "react-doctor/no-multi-comp": "off", "react-doctor/only-export-components": "off" diff --git a/package.json b/package.json index 9193a9bd..2ce6adee 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "clean": "turbo clean", "check:circular": "turbo check:circular", "registry:build": "turbo registry:build", + "tokens:generate": "pnpm -F @vllnt/design-source tokens:generate", + "tokens:check": "pnpm -F @vllnt/design-source tokens:check", + "ci:native": "pnpm tokens:check && pnpm -F @vllnt/ui-core lint && pnpm -F @vllnt/ui-native lint && pnpm -F @vllnt/ui-native-catalog lint && pnpm -F @vllnt/ui-core typecheck && pnpm -F @vllnt/ui-native typecheck && pnpm --filter '@vllnt/ui-native...' build && pnpm -F @vllnt/ui-core test:once && pnpm -F @vllnt/ui-native test:once && pnpm -F @vllnt/ui-native boundaries:check && pnpm -F @vllnt/ui-core pack:check && pnpm -F @vllnt/ui-native pack:check && pnpm -F @vllnt/ui-native-catalog typecheck && pnpm -F @vllnt/ui-native-catalog run doctor && pnpm -F @vllnt/ui-native-catalog test:once && pnpm -F @vllnt/ui-native-catalog build", "doctor": "npx --yes react-doctor . --offline", "doctor:full": "npx --yes react-doctor . --offline --full --verbose", "doctor:json": "npx --yes react-doctor . --offline --json > .react-doctor.json", @@ -31,6 +34,7 @@ "doctor:score": "npx --yes react-doctor . --offline --score" }, "devDependencies": { + "madge": "^8.0.0", "turbo": "^2.4.4" }, "pnpm": { @@ -66,7 +70,8 @@ "brace-expansion@>=4.0.0 <5.0.6": "5.0.6", "lodash": ">=4.18.1", "flatted": ">=3.4.2", - "postcss": ">=8.5.10" + "postcss": ">=8.5.10", + "ws@>=8.0.0 <8.21.0": "8.21.3" } } } diff --git a/packages/design/README.md b/packages/design/README.md index 7491aa35..37ab7658 100644 --- a/packages/design/README.md +++ b/packages/design/README.md @@ -1,9 +1,20 @@ # VLLNT UI Design Tokens `tokens.json` is the machine-readable companion to the root `DESIGN.md` guide. -It mirrors the public CSS variables in `packages/ui/themes/default.css` and adds -the typography, spacing, radius, elevation, motion, and iconography rules agents -need for consistent generated UI. +It is the authored source for the public web CSS variables and the generated +React Native theme in `@vllnt/ui-core`. `component-contracts.json` defines the +portable option names shared by the web and native pilot components. + +After editing either source, regenerate committed artifacts from the repository +root: + +```bash +pnpm -F @vllnt/design-source tokens:generate +pnpm -F @vllnt/design-source tokens:check +``` + +The generator preserves the existing `@vllnt/ui` CSS entry points and converts +OKLCH colors to sRGB for React Native, where OKLCH is not reliably supported. ## Schema @@ -12,8 +23,8 @@ The token file follows `tokens.schema.json`: - `name`: fixed library name, `VLLNT UI`. - `version`: target library version for the token contract. - `source`: pointers back to the human guide and CSS theme implementation. -- `color.semantic`: CSS variable names, light/dark HSL channels, and intended roles. -- `typography.scale`: font size, line height, and weight for canonical text styles. +- `color.semantic`: CSS variable names, light/dark OKLCH channels, and intended roles. +- `typography.scale`: font size plus explicit font-size and line-height CSS variables. - `spacing.scale`: 4-point spacing tokens mapped to rem values. - `radius`: allowed radius tokens. - `elevation`: allowed shadow tokens. diff --git a/packages/design/component-contracts.json b/packages/design/component-contracts.json new file mode 100644 index 00000000..986eeacb --- /dev/null +++ b/packages/design/component-contracts.json @@ -0,0 +1,38 @@ +{ + "$schema": "./component-contracts.schema.json", + "version": "0.1.0", + "components": { + "badge": { + "variants": ["default", "destructive", "outline", "secondary"] + }, + "button": { + "sizes": ["default", "icon", "lg", "sm"], + "variants": [ + "default", + "destructive", + "ghost", + "link", + "outline", + "secondary" + ] + }, + "card": { + "parts": [ + "Card", + "CardHeader", + "CardTitle", + "CardDescription", + "CardContent", + "CardFooter" + ] + }, + "heading": { + "levels": [1, 2, 3, 4, 5, 6] + }, + "text": { + "sizes": ["base", "caption", "lead", "small"], + "tones": ["default", "muted"], + "weights": ["medium", "normal", "semibold"] + } + } +} diff --git a/packages/design/component-contracts.schema.json b/packages/design/component-contracts.schema.json new file mode 100644 index 00000000..feabebf6 --- /dev/null +++ b/packages/design/component-contracts.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "VLLNT UI portable component contracts", + "type": "object", + "required": ["version", "components"], + "properties": { + "$schema": { "type": "string" }, + "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" }, + "components": { + "type": "object", + "required": ["badge", "button", "card", "heading", "text"], + "properties": { + "badge": { "$ref": "#/$defs/variants" }, + "button": { + "type": "object", + "required": ["sizes", "variants"], + "properties": { + "sizes": { "$ref": "#/$defs/stringList" }, + "variants": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + }, + "card": { + "type": "object", + "required": ["parts"], + "properties": { + "parts": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + }, + "heading": { + "type": "object", + "required": ["levels"], + "properties": { + "levels": { + "type": "array", + "items": { "type": "integer", "minimum": 1, "maximum": 6 }, + "minItems": 6, + "maxItems": 6, + "uniqueItems": true + } + }, + "additionalProperties": false + }, + "text": { + "type": "object", + "required": ["sizes", "tones", "weights"], + "properties": { + "sizes": { "$ref": "#/$defs/stringList" }, + "tones": { "$ref": "#/$defs/stringList" }, + "weights": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "$defs": { + "stringList": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1, + "uniqueItems": true + }, + "variants": { + "type": "object", + "required": ["variants"], + "properties": { + "variants": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/packages/design/package.json b/packages/design/package.json new file mode 100644 index 00000000..4cf2fd34 --- /dev/null +++ b/packages/design/package.json @@ -0,0 +1,13 @@ +{ + "name": "@vllnt/design-source", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "node scripts/generate-tokens.mjs --check", + "lint": "node scripts/generate-tokens.mjs --check", + "test:once": "node scripts/generate-tokens.mjs --check", + "tokens:check": "node scripts/generate-tokens.mjs --check", + "tokens:generate": "node scripts/generate-tokens.mjs" + } +} diff --git a/packages/design/scripts/generate-tokens.mjs b/packages/design/scripts/generate-tokens.mjs new file mode 100644 index 00000000..cc1595c6 --- /dev/null +++ b/packages/design/scripts/generate-tokens.mjs @@ -0,0 +1,409 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDirectory = dirname(fileURLToPath(import.meta.url)); +const designDirectory = join(scriptDirectory, ".."); +const repositoryRoot = join(designDirectory, "..", ".."); +const checkOnly = process.argv.includes("--check"); + +const paths = { + contracts: join(designDirectory, "component-contracts.json"), + contractsSchema: join(designDirectory, "component-contracts.schema.json"), + coreContracts: join(repositoryRoot, "packages/ui-core/component-contracts.json"), + coreContractsSchema: join( + repositoryRoot, + "packages/ui-core/component-contracts.schema.json", + ), + coreGenerated: join( + repositoryRoot, + "packages/ui-core/src/generated/design-tokens.ts", + ), + coreTokens: join(repositoryRoot, "packages/ui-core/tokens.json"), + coreTokensSchema: join(repositoryRoot, "packages/ui-core/tokens.schema.json"), + tokens: join(designDirectory, "tokens.json"), + tokensSchema: join(designDirectory, "tokens.schema.json"), + uiDefaultTheme: join(repositoryRoot, "packages/ui/themes/default.css"), + uiStyles: join(repositoryRoot, "packages/ui/styles.css"), +}; + +const parseJson = async (path) => JSON.parse(await readFile(path, "utf8")); +const [designTokens, componentContracts] = await Promise.all([ + parseJson(paths.tokens), + parseJson(paths.contracts), +]); + +// Renderer-specific accessibility adjustments preserve the authored web theme: +// native uses small destructive labels and DESIGN.md bans pure-black backgrounds. +const nativeColorOverrides = { + dark: { + background: "#050505", + }, + light: { + destructive: "#c92f32", + }, +}; + +const expectedSemanticColors = [ + "background", + "foreground", + "card", + "cardForeground", + "popover", + "popoverForeground", + "primary", + "primaryForeground", + "secondary", + "secondaryForeground", + "muted", + "mutedForeground", + "accent", + "accentForeground", + "destructive", + "destructiveForeground", + "border", + "input", + "ring", +]; + +function assert(condition, message) { + if (!condition) throw new Error(message); +} + +function validateSource() { + assert(designTokens.name === "VLLNT UI", 'tokens.json name must be "VLLNT UI".'); + assert( + designTokens.color?.format === "oklch-channel", + 'tokens.json color.format must be "oklch-channel".', + ); + + const colorEntries = Object.entries(designTokens.color?.semantic ?? {}); + assert( + colorEntries.map(([name]) => name).join(",") === + expectedSemanticColors.join(","), + "tokens.json semantic color names or order changed unexpectedly.", + ); + + const variables = new Set(); + for (const [name, color] of colorEntries) { + assert( + /^--[a-z][a-z0-9-]*$/.test(color.cssVariable), + `Color ${name} has an invalid CSS variable.`, + ); + assert(!variables.has(color.cssVariable), `Duplicate CSS variable ${color.cssVariable}.`); + variables.add(color.cssVariable); + for (const mode of ["light", "dark"]) { + const channels = color[mode].split(/\s+/).map(Number); + assert( + channels.length === 3 && channels.every(Number.isFinite), + `Color ${name}.${mode} must contain three OKLCH channels.`, + ); + assert( + channels[0] >= 0 && channels[0] <= 1 && channels[1] >= 0, + `Color ${name}.${mode} has out-of-range OKLCH channels.`, + ); + } + } + + for (const [name, step] of Object.entries(designTokens.typography?.scale ?? {})) { + assert(step.cssVariable, `Typography ${name} is missing cssVariable.`); + assert( + step.lineHeightCssVariable, + `Typography ${name} is missing lineHeightCssVariable.`, + ); + } + + for (const [component, contract] of Object.entries( + componentContracts.components ?? {}, + )) { + for (const value of Object.values(contract)) { + assert(Array.isArray(value) && value.length > 0, `${component} contract lists cannot be empty.`); + assert(new Set(value).size === value.length, `${component} contract lists must be unique.`); + } + } +} + +function cssFontFamily(value) { + return value.replaceAll(/'([^']+)'/g, '"$1"'); +} + +function declarationLines(mode, indentation) { + return Object.values(designTokens.color.semantic).map( + (color) => `${indentation}${color.cssVariable}: ${color[mode]};`, + ); +} + +function typographyLines(indentation) { + const lines = [ + `${indentation}--radius: ${designTokens.radius.md};`, + `${indentation}/* Typography — theme-overridable font tokens (issue #465). */`, + ]; + for (const family of Object.values(designTokens.typography.fontFamily)) { + lines.push( + `${indentation}${family.cssVariable}: ${cssFontFamily(family.value)};`, + ); + } + for (const weight of Object.values(designTokens.typography.fontWeight)) { + if (weight.cssVariable) { + lines.push(`${indentation}${weight.cssVariable}: ${weight.value};`); + } + } + for (const step of Object.values(designTokens.typography.scale)) { + lines.push(`${indentation}${step.cssVariable}: ${step.fontSize};`); + lines.push( + `${indentation}${step.lineHeightCssVariable}: ${step.lineHeight};`, + ); + } + return lines; +} + +function renderDefaultTheme() { + return [ + '/* Default theme variables. Values are OKLCH channels ("L C H"), consumed as oklch(var(--x)). */', + ":root {", + ...declarationLines("light", " "), + ...typographyLines(" "), + "}", + ".dark {", + ...declarationLines("dark", " "), + "}", + "", + ].join("\n"); +} + +function renderStylesThemeBlock() { + const typography = typographyLines(" "); + const commentIndex = typography.findIndex((line) => line.includes("Typography")); + typography.splice( + commentIndex, + 1, + " /* Typography — theme-overridable font tokens (issue #465). Override any of", + " these on a theme scope to restyle the Text/Heading/Display/Prose primitives", + " without editing the library. Defaults reproduce the house style (sans, 600). */", + ); + + return [ + "/* Default theme variables */", + "@layer base {", + " :root {", + ...declarationLines("light", " "), + ...typography, + " }", + " .dark {", + ...declarationLines("dark", " "), + " }", + "}", + "", + "", + ].join("\n"); +} + +function replaceStylesThemeBlock(source) { + const startMarker = "/* Default theme variables */\n@layer base {"; + const endMarker = "@layer base {\n * {"; + const start = source.indexOf(startMarker); + const end = source.indexOf(endMarker, start + startMarker.length); + assert(start >= 0 && end > start, "Could not locate the generated token block in styles.css."); + return `${source.slice(0, start)}${renderStylesThemeBlock()}${source.slice(end)}`; +} + +function remToPoints(value) { + const match = /^(-?[0-9]+(?:\.[0-9]+)?)rem$/.exec(value); + assert(match, `Expected a rem value, received ${value}.`); + return Number(match[1]) * 16; +} + +function milliseconds(value) { + const match = /^([0-9]+(?:\.[0-9]+)?)ms$/.exec(value); + assert(match, `Expected an ms value, received ${value}.`); + return Number(match[1]); +} + +function relativeLuminance(hex) { + const channels = hex + .slice(1) + .match(/.{2}/g) + .map((channel) => Number.parseInt(channel, 16) / 255) + .map((channel) => + channel <= 0.040_45 + ? channel / 12.92 + : ((channel + 0.055) / 1.055) ** 2.4, + ); + return ( + 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2] + ); +} + +function contrastRatio(first, second) { + const light = Math.max(relativeLuminance(first), relativeLuminance(second)); + const dark = Math.min(relativeLuminance(first), relativeLuminance(second)); + return (light + 0.05) / (dark + 0.05); +} + +function oklchToHex(channels) { + const [lightness, chroma, hue] = channels.split(/\s+/).map(Number); + const radians = (hue * Math.PI) / 180; + const a = chroma * Math.cos(radians); + const b = chroma * Math.sin(radians); + const lPrime = lightness + 0.396_337_777_4 * a + 0.215_803_757_3 * b; + const mPrime = lightness - 0.105_561_345_8 * a - 0.063_854_172_8 * b; + const sPrime = lightness - 0.089_484_177_5 * a - 1.291_485_548 * b; + const l = lPrime ** 3; + const m = mPrime ** 3; + const s = sPrime ** 3; + const linear = [ + 4.076_741_662_1 * l - 3.307_711_591_3 * m + 0.230_969_929_2 * s, + -1.268_438_004_6 * l + 2.609_757_401_1 * m - 0.341_319_396_5 * s, + -0.004_196_086_3 * l - 0.703_418_614_7 * m + 1.707_614_701 * s, + ]; + const encoded = linear.map((channel) => { + const value = + channel <= 0.003_130_8 + ? 12.92 * channel + : 1.055 * channel ** (1 / 2.4) - 0.055; + return Math.round(Math.min(1, Math.max(0, value)) * 255); + }); + return `#${encoded.map((value) => value.toString(16).padStart(2, "0")).join("")}`; +} + +function nativeTokens() { + const colors = { dark: {}, light: {} }; + for (const [name, color] of Object.entries(designTokens.color.semantic)) { + colors.light[name] = + nativeColorOverrides.light[name] ?? oklchToHex(color.light); + colors.dark[name] = + nativeColorOverrides.dark[name] ?? oklchToHex(color.dark); + } + for (const mode of ["light", "dark"]) { + for (const [surface, foreground] of [ + ["card", "cardForeground"], + ["destructive", "destructiveForeground"], + ["primary", "primaryForeground"], + ["secondary", "secondaryForeground"], + ]) { + assert( + contrastRatio(colors[mode][surface], colors[mode][foreground]) >= 4.5, + `Native ${mode} ${surface}/${foreground} contrast must be at least 4.5:1.`, + ); + } + } + + const spacing = Object.fromEntries( + Object.entries(designTokens.spacing.scale).map(([name, value]) => [ + name, + remToPoints(value), + ]), + ); + const radius = Object.fromEntries( + Object.entries(designTokens.radius).map(([name, value]) => [ + name, + value.endsWith("rem") ? remToPoints(value) : Number.parseFloat(value), + ]), + ); + const typeScale = Object.fromEntries( + Object.entries(designTokens.typography.scale).map(([name, step]) => { + const fontSize = remToPoints(step.fontSize); + return [ + name, + { + fontSize, + lineHeight: Number((fontSize * Number(step.lineHeight)).toFixed(3)), + }, + ]; + }), + ); + const fontWeight = Object.fromEntries( + Object.entries(designTokens.typography.fontWeight).map(([name, weight]) => [ + name, + weight.value, + ]), + ); + const duration = Object.fromEntries( + Object.entries(designTokens.motion.duration).map(([name, value]) => [ + name, + milliseconds(value), + ]), + ); + + return { + color: colors, + motion: { duration }, + radius, + spacing, + typography: { fontWeight, scale: typeScale }, + }; +} + +function renderGeneratedTypeScript() { + const serialize = (value) => JSON.stringify(value, null, 2); + return [ + "/* This file is generated from packages/design. Do not edit directly. */", + "", + `export const designTokens = ${serialize(designTokens)} as const;`, + "", + `export const componentContracts = ${serialize(componentContracts)} as const;`, + "", + `export const nativeTokens = ${serialize(nativeTokens())} as const;`, + "", + "export type BadgeVariant =", + " (typeof componentContracts.components.badge.variants)[number];", + "export type ButtonSize =", + " (typeof componentContracts.components.button.sizes)[number];", + "export type ButtonVariant =", + " (typeof componentContracts.components.button.variants)[number];", + "export type CardPart =", + " (typeof componentContracts.components.card.parts)[number];", + "export type HeadingLevel =", + " (typeof componentContracts.components.heading.levels)[number];", + "export type TextSize =", + " (typeof componentContracts.components.text.sizes)[number];", + "export type TextTone =", + " (typeof componentContracts.components.text.tones)[number];", + "export type TextWeight =", + " (typeof componentContracts.components.text.weights)[number];", + "export type SemanticColorName = keyof typeof nativeTokens.color.light;", + "", + ].join("\n"); +} + +validateSource(); + +const currentStyles = await readFile(paths.uiStyles, "utf8"); +const outputs = new Map([ + [paths.uiDefaultTheme, renderDefaultTheme()], + [paths.uiStyles, replaceStylesThemeBlock(currentStyles)], + [paths.coreGenerated, renderGeneratedTypeScript()], + [paths.coreTokens, await readFile(paths.tokens, "utf8")], + [paths.coreTokensSchema, await readFile(paths.tokensSchema, "utf8")], + [paths.coreContracts, await readFile(paths.contracts, "utf8")], + [paths.coreContractsSchema, await readFile(paths.contractsSchema, "utf8")], +]); + +const stale = []; +for (const [path, expected] of outputs) { + let actual = ""; + try { + actual = await readFile(path, "utf8"); + } catch { + actual = ""; + } + if (actual === expected) continue; + stale.push(path.slice(repositoryRoot.length + 1)); + if (!checkOnly) { + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, expected); + } +} + +if (checkOnly && stale.length > 0) { + console.error("Generated token artifacts are stale:"); + for (const path of stale) console.error(` - ${path}`); + console.error("Run: pnpm tokens:generate"); + process.exit(1); +} + +if (stale.length === 0) { + console.log("Token artifacts are in sync."); +} else { + console.log(`Generated ${stale.length} token artifact(s).`); +} diff --git a/packages/design/tokens.json b/packages/design/tokens.json index 1f77547e..83ad4021 100644 --- a/packages/design/tokens.json +++ b/packages/design/tokens.json @@ -161,57 +161,68 @@ "display": { "cssVariable": "--font-size-display", "fontSize": "3.75rem", - "lineHeight": "1.05" + "lineHeight": "1.05", + "lineHeightCssVariable": "--line-height-display" }, "h1": { "cssVariable": "--font-size-h1", "fontSize": "3rem", - "lineHeight": "1.1" + "lineHeight": "1.1", + "lineHeightCssVariable": "--line-height-h1" }, "h2": { "cssVariable": "--font-size-h2", "fontSize": "2.25rem", - "lineHeight": "1.2" + "lineHeight": "1.2", + "lineHeightCssVariable": "--line-height-h2" }, "h3": { "cssVariable": "--font-size-h3", "fontSize": "1.875rem", - "lineHeight": "1.25" + "lineHeight": "1.25", + "lineHeightCssVariable": "--line-height-h3" }, "h4": { "cssVariable": "--font-size-h4", "fontSize": "1.5rem", - "lineHeight": "1.3" + "lineHeight": "1.3", + "lineHeightCssVariable": "--line-height-h4" }, "h5": { "cssVariable": "--font-size-h5", "fontSize": "1.25rem", - "lineHeight": "1.4" + "lineHeight": "1.4", + "lineHeightCssVariable": "--line-height-h5" }, "h6": { "cssVariable": "--font-size-h6", "fontSize": "1.125rem", - "lineHeight": "1.5" + "lineHeight": "1.5", + "lineHeightCssVariable": "--line-height-h6" }, "bodyLarge": { "cssVariable": "--font-size-body-lg", "fontSize": "1.125rem", - "lineHeight": "1.7" + "lineHeight": "1.7", + "lineHeightCssVariable": "--line-height-body-lg" }, "body": { "cssVariable": "--font-size-body", "fontSize": "1rem", - "lineHeight": "1.6" + "lineHeight": "1.6", + "lineHeightCssVariable": "--line-height-body" }, "bodySmall": { "cssVariable": "--font-size-body-sm", "fontSize": "0.875rem", - "lineHeight": "1.5" + "lineHeight": "1.5", + "lineHeightCssVariable": "--line-height-body-sm" }, "caption": { "cssVariable": "--font-size-caption", "fontSize": "0.75rem", - "lineHeight": "1.4" + "lineHeight": "1.4", + "lineHeightCssVariable": "--line-height-caption" } } }, diff --git a/packages/design/tokens.schema.json b/packages/design/tokens.schema.json index 7ef2a017..c56d81c3 100644 --- a/packages/design/tokens.schema.json +++ b/packages/design/tokens.schema.json @@ -2,50 +2,160 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "VLLNT UI design tokens", "type": "object", - "required": ["name", "version", "source", "color", "typography", "spacing", "radius", "elevation", "motion", "iconography"], + "required": [ + "name", + "version", + "source", + "color", + "typography", + "spacing", + "radius", + "elevation", + "motion", + "iconography" + ], "properties": { - "$schema": { - "type": "string" - }, - "name": { - "const": "VLLNT UI" - }, - "version": { - "type": "string" - }, + "$schema": { "type": "string" }, + "name": { "const": "VLLNT UI" }, + "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" }, "source": { "type": "object", "required": ["guide", "theme"], "properties": { - "guide": { - "type": "string" - }, - "theme": { - "type": "string" - } + "guide": { "type": "string" }, + "theme": { "type": "string" } }, "additionalProperties": false }, "color": { - "type": "object" + "type": "object", + "required": ["format", "semantic"], + "properties": { + "format": { "const": "oklch-channel" }, + "semantic": { + "type": "object", + "minProperties": 1, + "additionalProperties": { "$ref": "#/$defs/semanticColor" } + } + }, + "additionalProperties": false }, "typography": { - "type": "object" + "type": "object", + "required": ["fontFamily", "fontWeight", "scale"], + "properties": { + "fontFamily": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/fontFamily" } + }, + "fontWeight": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/fontWeight" } + }, + "scale": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/typeScale" } + } + }, + "additionalProperties": false }, "spacing": { - "type": "object" - }, - "radius": { - "type": "object" - }, - "elevation": { - "type": "object" + "type": "object", + "required": ["unit", "scale"], + "properties": { + "unit": { "$ref": "#/$defs/cssLength" }, + "scale": { "$ref": "#/$defs/stringMap" } + }, + "additionalProperties": false }, + "radius": { "$ref": "#/$defs/stringMap" }, + "elevation": { "$ref": "#/$defs/stringMap" }, "motion": { - "type": "object" + "type": "object", + "required": ["duration", "easing", "reducedMotion"], + "properties": { + "duration": { "$ref": "#/$defs/stringMap" }, + "easing": { "$ref": "#/$defs/stringMap" }, + "reducedMotion": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false }, "iconography": { - "type": "object" + "type": "object", + "required": ["library", "size", "strokeWidth", "color"], + "properties": { + "library": { "type": "string", "minLength": 1 }, + "size": { "$ref": "#/$defs/stringMap" }, + "strokeWidth": { "type": "number", "exclusiveMinimum": 0 }, + "color": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + } + }, + "$defs": { + "cssLength": { + "type": "string", + "pattern": "^-?[0-9]+(?:\\.[0-9]+)?(?:px|rem|em|%)$" + }, + "cssVariable": { + "type": "string", + "pattern": "^--[a-z][a-z0-9-]*$" + }, + "fontFamily": { + "type": "object", + "required": ["cssVariable", "value", "role"], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "value": { "type": "string", "minLength": 1 }, + "role": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "fontWeight": { + "type": "object", + "required": ["value"], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "value": { "type": "integer", "minimum": 1, "maximum": 1000 }, + "role": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "oklchChannel": { + "type": "string", + "pattern": "^(?:0(?:\\.[0-9]+)?|1(?:\\.0+)?) (?:0(?:\\.[0-9]+)?|1(?:\\.0+)?) -?[0-9]+(?:\\.[0-9]+)?$" + }, + "semanticColor": { + "type": "object", + "required": ["cssVariable", "light", "dark", "role"], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "light": { "$ref": "#/$defs/oklchChannel" }, + "dark": { "$ref": "#/$defs/oklchChannel" }, + "role": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "stringMap": { + "type": "object", + "minProperties": 1, + "additionalProperties": { "type": "string", "minLength": 1 } + }, + "typeScale": { + "type": "object", + "required": [ + "cssVariable", + "fontSize", + "lineHeight", + "lineHeightCssVariable" + ], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "fontSize": { "$ref": "#/$defs/cssLength" }, + "lineHeight": { "type": "string", "pattern": "^[0-9]+(?:\\.[0-9]+)?$" }, + "lineHeightCssVariable": { "$ref": "#/$defs/cssVariable" } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/packages/ui-core/CHANGELOG.md b/packages/ui-core/CHANGELOG.md new file mode 100644 index 00000000..8052dd7e --- /dev/null +++ b/packages/ui-core/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to `@vllnt/ui-core` are documented in this file. + +## [Unreleased] + +### Added + +- Initial platform-neutral token, theme, platform metadata, and portable component contracts. diff --git a/packages/ui-core/README.md b/packages/ui-core/README.md new file mode 100644 index 00000000..acd382e3 --- /dev/null +++ b/packages/ui-core/README.md @@ -0,0 +1,29 @@ +# @vllnt/ui-core + +Platform-neutral design tokens and portable component contracts shared by the VLLNT UI web and React Native renderers. + +> Experimental. This package is published only on the `canary` npm tag while the native renderer is validated. + +## Install + +```bash +pnpm add @vllnt/ui-core@canary +``` + +## Use + +```ts +import { + createNativeTheme, + designTokens, + type ButtonVariant, +} from "@vllnt/ui-core"; + +const theme = createNativeTheme("dark", { + colors: { primary: "#f5f5f5" }, +}); +``` + +Exports include the authored token object, generated React Native-compatible themes, and renderer-neutral option unions for the pilot components. The JSON contracts are also available from `@vllnt/ui-core/tokens.json` and `@vllnt/ui-core/contracts.json`. + +`packages/design/tokens.json` and `packages/design/component-contracts.json` are the authored sources. Run `pnpm -F @vllnt/design-source tokens:generate` after changing either file. Generated artifacts are committed and checked for drift in CI. diff --git a/packages/ui-core/component-contracts.json b/packages/ui-core/component-contracts.json new file mode 100644 index 00000000..986eeacb --- /dev/null +++ b/packages/ui-core/component-contracts.json @@ -0,0 +1,38 @@ +{ + "$schema": "./component-contracts.schema.json", + "version": "0.1.0", + "components": { + "badge": { + "variants": ["default", "destructive", "outline", "secondary"] + }, + "button": { + "sizes": ["default", "icon", "lg", "sm"], + "variants": [ + "default", + "destructive", + "ghost", + "link", + "outline", + "secondary" + ] + }, + "card": { + "parts": [ + "Card", + "CardHeader", + "CardTitle", + "CardDescription", + "CardContent", + "CardFooter" + ] + }, + "heading": { + "levels": [1, 2, 3, 4, 5, 6] + }, + "text": { + "sizes": ["base", "caption", "lead", "small"], + "tones": ["default", "muted"], + "weights": ["medium", "normal", "semibold"] + } + } +} diff --git a/packages/ui-core/component-contracts.schema.json b/packages/ui-core/component-contracts.schema.json new file mode 100644 index 00000000..feabebf6 --- /dev/null +++ b/packages/ui-core/component-contracts.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "VLLNT UI portable component contracts", + "type": "object", + "required": ["version", "components"], + "properties": { + "$schema": { "type": "string" }, + "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" }, + "components": { + "type": "object", + "required": ["badge", "button", "card", "heading", "text"], + "properties": { + "badge": { "$ref": "#/$defs/variants" }, + "button": { + "type": "object", + "required": ["sizes", "variants"], + "properties": { + "sizes": { "$ref": "#/$defs/stringList" }, + "variants": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + }, + "card": { + "type": "object", + "required": ["parts"], + "properties": { + "parts": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + }, + "heading": { + "type": "object", + "required": ["levels"], + "properties": { + "levels": { + "type": "array", + "items": { "type": "integer", "minimum": 1, "maximum": 6 }, + "minItems": 6, + "maxItems": 6, + "uniqueItems": true + } + }, + "additionalProperties": false + }, + "text": { + "type": "object", + "required": ["sizes", "tones", "weights"], + "properties": { + "sizes": { "$ref": "#/$defs/stringList" }, + "tones": { "$ref": "#/$defs/stringList" }, + "weights": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "$defs": { + "stringList": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1, + "uniqueItems": true + }, + "variants": { + "type": "object", + "required": ["variants"], + "properties": { + "variants": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/packages/ui-core/eslint.config.js b/packages/ui-core/eslint.config.js new file mode 100644 index 00000000..bf4affc0 --- /dev/null +++ b/packages/ui-core/eslint.config.js @@ -0,0 +1,21 @@ +import { nodejs } from "@vllnt/eslint-config"; + +export default [ + { + ignores: [ + "dist/**", + "node_modules/**", + "scripts/**", + "src/generated/**", + "eslint.config.js", + "tsup.config.ts", + "vitest.config.ts", + ], + }, + ...nodejs, + { + linterOptions: { + reportUnusedDisableDirectives: "error", + }, + }, +]; diff --git a/packages/ui-core/package.json b/packages/ui-core/package.json new file mode 100644 index 00000000..01d10535 --- /dev/null +++ b/packages/ui-core/package.json @@ -0,0 +1,75 @@ +{ + "name": "@vllnt/ui-core", + "version": "0.1.0", + "description": "Platform-neutral design tokens and component contracts for VLLNT UI renderers", + "license": "MIT", + "author": "vllnt", + "homepage": "https://ui.vllnt.com", + "repository": { + "type": "git", + "url": "git+https://github.com/vllnt/ui.git", + "directory": "packages/ui-core" + }, + "bugs": { + "url": "https://github.com/vllnt/ui/issues" + }, + "keywords": ["design-system", "design-tokens", "typescript", "ui"], + "type": "module", + "main": "./src/index.ts", + "module": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./src/index.ts" + }, + "./tokens.json": "./tokens.json", + "./contracts.json": "./component-contracts.json" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org", + "access": "public", + "tag": "canary", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./tokens.json": "./tokens.json", + "./contracts.json": "./component-contracts.json" + } + }, + "files": [ + "dist", + "tokens.json", + "tokens.schema.json", + "component-contracts.json", + "component-contracts.schema.json", + "CHANGELOG.md", + "README.md" + ], + "sideEffects": false, + "scripts": { + "build": "pnpm tokens:check && tsup", + "clean": "rm -rf dist node_modules coverage", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "pack:check": "node scripts/check-packed-package.mjs", + "test": "vitest", + "test:once": "vitest run", + "tokens:check": "node ../design/scripts/generate-tokens.mjs --check", + "tokens:generate": "node ../design/scripts/generate-tokens.mjs", + "typecheck": "tsc --noEmit --project tsconfig.build.json" + }, + "devDependencies": { + "@vllnt/eslint-config": "^1.0.0", + "@vllnt/typescript": "^1.0.0", + "eslint": "^9.39.1", + "tsup": "^8.5.0", + "typescript": "^5.9.3", + "vitest": "^4.1.9" + } +} diff --git a/packages/ui-core/scripts/check-packed-package.mjs b/packages/ui-core/scripts/check-packed-package.mjs new file mode 100644 index 00000000..462b0cef --- /dev/null +++ b/packages/ui-core/scripts/check-packed-package.mjs @@ -0,0 +1,87 @@ +import { mkdtemp, readFile, rm, stat } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; + +const packageDirectory = resolve(import.meta.dirname, ".."); +const temporaryDirectory = await mkdtemp(join(tmpdir(), "vllnt-ui-core-pack-")); + +function run(command, arguments_, cwd = packageDirectory) { + const result = spawnSync(command, arguments_, { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "inherit"], + }); + if (result.status !== 0) { + throw new Error(`${command} ${arguments_.join(" ")} failed.`); + } + return result.stdout.trim(); +} + +async function assertFile(path, field) { + try { + const metadata = await stat(path); + if (!metadata.isFile()) throw new Error(); + } catch { + throw new Error(`Packed ${field} target is missing: ${path}`); + } +} + +try { + const output = run("pnpm", [ + "pack", + "--pack-destination", + temporaryDirectory, + ]); + const tarball = output.split("\n").at(-1); + if (!tarball?.endsWith(".tgz")) { + throw new Error(`Could not identify packed tarball from: ${output}`); + } + + run("tar", [ + "-xzf", + resolve(packageDirectory, tarball), + "-C", + temporaryDirectory, + ]); + const packedDirectory = join(temporaryDirectory, "package"); + const manifest = JSON.parse( + await readFile(join(packedDirectory, "package.json"), "utf8"), + ); + + for (const field of ["main", "module", "types"]) { + const target = manifest[field]; + if (typeof target !== "string" || target.startsWith("./src/")) { + throw new Error(`Packed ${field} must target dist; received ${target}.`); + } + await assertFile(join(packedDirectory, target), field); + } + + const rootExport = manifest.exports?.["."]; + for (const condition of ["import", "types"]) { + const target = rootExport?.[condition]; + if (typeof target !== "string" || target.startsWith("./src/")) { + throw new Error( + `Packed exports[\".\"].${condition} must target dist; received ${target}.`, + ); + } + await assertFile(join(packedDirectory, target), `exports.${condition}`); + } + + const tokensExport = manifest.exports?.["./tokens.json"]; + const contractsExport = manifest.exports?.["./contracts.json"]; + if (tokensExport !== "./tokens.json") { + throw new Error(`Packed tokens export is invalid: ${tokensExport}.`); + } + if (contractsExport !== "./component-contracts.json") { + throw new Error(`Packed contracts export is invalid: ${contractsExport}.`); + } + await assertFile(join(packedDirectory, tokensExport), "tokens export"); + await assertFile(join(packedDirectory, contractsExport), "contracts export"); + + console.log( + `Packed core package resolves runtime, types, tokens, and contracts from published files (${manifest.version}).`, + ); +} finally { + await rm(temporaryDirectory, { force: true, recursive: true }); +} diff --git a/packages/ui-core/src/generated/design-tokens.ts b/packages/ui-core/src/generated/design-tokens.ts new file mode 100644 index 00000000..b72d5252 --- /dev/null +++ b/packages/ui-core/src/generated/design-tokens.ts @@ -0,0 +1,495 @@ +/* This file is generated from packages/design. Do not edit directly. */ + +export const designTokens = { + "$schema": "./tokens.schema.json", + "name": "VLLNT UI", + "version": "0.4.0", + "source": { + "guide": "../../DESIGN.md", + "theme": "../ui/themes/default.css" + }, + "color": { + "format": "oklch-channel", + "semantic": { + "background": { + "cssVariable": "--background", + "light": "1 0 0", + "dark": "0 0 0", + "role": "Page and app surface" + }, + "foreground": { + "cssVariable": "--foreground", + "light": "0.1445 0 0", + "dark": "0.9848 0 0", + "role": "Primary text and icon color" + }, + "card": { + "cssVariable": "--card", + "light": "1 0 0", + "dark": "0.1445 0 0", + "role": "Card and contained surface" + }, + "cardForeground": { + "cssVariable": "--card-foreground", + "light": "0.1445 0 0", + "dark": "0.9848 0 0", + "role": "Text on card surfaces" + }, + "popover": { + "cssVariable": "--popover", + "light": "1 0 0", + "dark": "0.1445 0 0", + "role": "Popover, menu, and floating surface" + }, + "popoverForeground": { + "cssVariable": "--popover-foreground", + "light": "0.1445 0 0", + "dark": "0.9848 0 0", + "role": "Text on popover surfaces" + }, + "primary": { + "cssVariable": "--primary", + "light": "0.2044 0 0", + "dark": "0.9848 0 0", + "role": "Primary action surface" + }, + "primaryForeground": { + "cssVariable": "--primary-foreground", + "light": "0.9848 0 0", + "dark": "0.2044 0 0", + "role": "Text on primary action surfaces" + }, + "secondary": { + "cssVariable": "--secondary", + "light": "0.9703 0 0", + "dark": "0.2686 0 0", + "role": "Secondary action surface" + }, + "secondaryForeground": { + "cssVariable": "--secondary-foreground", + "light": "0.2044 0 0", + "dark": "0.9848 0 0", + "role": "Text on secondary action surfaces" + }, + "muted": { + "cssVariable": "--muted", + "light": "0.9703 0 0", + "dark": "0.2686 0 0", + "role": "Subtle surface" + }, + "mutedForeground": { + "cssVariable": "--muted-foreground", + "light": "0.5555 0 0", + "dark": "0.7153 0 0", + "role": "Secondary text" + }, + "accent": { + "cssVariable": "--accent", + "light": "0.9703 0 0", + "dark": "0.2686 0 0", + "role": "Hover and active surface" + }, + "accentForeground": { + "cssVariable": "--accent-foreground", + "light": "0.2044 0 0", + "dark": "0.9848 0 0", + "role": "Text on accent surfaces" + }, + "destructive": { + "cssVariable": "--destructive", + "light": "0.6368 0.2078 25.326", + "dark": "0.3959 0.1331 25.721", + "role": "Destructive action and error surface" + }, + "destructiveForeground": { + "cssVariable": "--destructive-foreground", + "light": "0.9848 0 0", + "dark": "0.9848 0 0", + "role": "Text on destructive surfaces" + }, + "border": { + "cssVariable": "--border", + "light": "0.9219 0 0", + "dark": "0.2686 0 0", + "role": "Hairline divider" + }, + "input": { + "cssVariable": "--input", + "light": "0.9219 0 0", + "dark": "0.2686 0 0", + "role": "Input border" + }, + "ring": { + "cssVariable": "--ring", + "light": "0.1445 0 0", + "dark": "0.8697 0 0", + "role": "Focus ring" + } + } + }, + "typography": { + "fontFamily": { + "sans": { + "cssVariable": "--font-sans", + "value": "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif", + "role": "Body + UI text" + }, + "display": { + "cssVariable": "--font-display", + "value": "var(--font-sans)", + "role": "Heading + Display face — override per theme for a brand type identity (defaults to sans)" + }, + "mono": { + "cssVariable": "--font-mono", + "value": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", + "role": "Code + tabular" + } + }, + "fontWeight": { + "heading": { + "cssVariable": "--font-weight-heading", + "value": 600, + "role": "Heading primitive weight — theme-overridable" + }, + "display": { + "cssVariable": "--font-weight-display", + "value": 600, + "role": "Display primitive weight — theme-overridable" + }, + "body": { + "value": 400 + }, + "caption": { + "value": 500 + } + }, + "scale": { + "display": { + "cssVariable": "--font-size-display", + "fontSize": "3.75rem", + "lineHeight": "1.05", + "lineHeightCssVariable": "--line-height-display" + }, + "h1": { + "cssVariable": "--font-size-h1", + "fontSize": "3rem", + "lineHeight": "1.1", + "lineHeightCssVariable": "--line-height-h1" + }, + "h2": { + "cssVariable": "--font-size-h2", + "fontSize": "2.25rem", + "lineHeight": "1.2", + "lineHeightCssVariable": "--line-height-h2" + }, + "h3": { + "cssVariable": "--font-size-h3", + "fontSize": "1.875rem", + "lineHeight": "1.25", + "lineHeightCssVariable": "--line-height-h3" + }, + "h4": { + "cssVariable": "--font-size-h4", + "fontSize": "1.5rem", + "lineHeight": "1.3", + "lineHeightCssVariable": "--line-height-h4" + }, + "h5": { + "cssVariable": "--font-size-h5", + "fontSize": "1.25rem", + "lineHeight": "1.4", + "lineHeightCssVariable": "--line-height-h5" + }, + "h6": { + "cssVariable": "--font-size-h6", + "fontSize": "1.125rem", + "lineHeight": "1.5", + "lineHeightCssVariable": "--line-height-h6" + }, + "bodyLarge": { + "cssVariable": "--font-size-body-lg", + "fontSize": "1.125rem", + "lineHeight": "1.7", + "lineHeightCssVariable": "--line-height-body-lg" + }, + "body": { + "cssVariable": "--font-size-body", + "fontSize": "1rem", + "lineHeight": "1.6", + "lineHeightCssVariable": "--line-height-body" + }, + "bodySmall": { + "cssVariable": "--font-size-body-sm", + "fontSize": "0.875rem", + "lineHeight": "1.5", + "lineHeightCssVariable": "--line-height-body-sm" + }, + "caption": { + "cssVariable": "--font-size-caption", + "fontSize": "0.75rem", + "lineHeight": "1.4", + "lineHeightCssVariable": "--line-height-caption" + } + } + }, + "spacing": { + "unit": "4px", + "scale": { + "1": "0.25rem", + "2": "0.5rem", + "3": "0.75rem", + "4": "1rem", + "6": "1.5rem", + "8": "2rem", + "12": "3rem", + "16": "4rem" + } + }, + "radius": { + "none": "0", + "sm": "0.25rem", + "md": "0.5rem", + "lg": "0.75rem", + "full": "9999px" + }, + "elevation": { + "none": "none", + "sm": "0 1px 2px rgba(0, 0, 0, 0.05)", + "md": "0 4px 6px -1px rgba(0, 0, 0, 0.1)", + "lg": "0 10px 15px -3px rgba(0, 0, 0, 0.1)" + }, + "motion": { + "duration": { + "fast": "100ms", + "base": "200ms", + "slow": "300ms" + }, + "easing": { + "enter": "ease-out", + "exit": "ease-in", + "layout": "ease-in-out" + }, + "reducedMotion": "Collapse durations to 1ms or skip non-essential motion." + }, + "iconography": { + "library": "lucide-react", + "size": { + "default": "1rem", + "compact": "0.875rem", + "large": "1.25rem" + }, + "strokeWidth": 2, + "color": "currentColor" + } +} as const; + +export const componentContracts = { + "$schema": "./component-contracts.schema.json", + "version": "0.1.0", + "components": { + "badge": { + "variants": [ + "default", + "destructive", + "outline", + "secondary" + ] + }, + "button": { + "sizes": [ + "default", + "icon", + "lg", + "sm" + ], + "variants": [ + "default", + "destructive", + "ghost", + "link", + "outline", + "secondary" + ] + }, + "card": { + "parts": [ + "Card", + "CardHeader", + "CardTitle", + "CardDescription", + "CardContent", + "CardFooter" + ] + }, + "heading": { + "levels": [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + }, + "text": { + "sizes": [ + "base", + "caption", + "lead", + "small" + ], + "tones": [ + "default", + "muted" + ], + "weights": [ + "medium", + "normal", + "semibold" + ] + } + } +} as const; + +export const nativeTokens = { + "color": { + "dark": { + "background": "#050505", + "foreground": "#fafafa", + "card": "#0a0a0a", + "cardForeground": "#fafafa", + "popover": "#0a0a0a", + "popoverForeground": "#fafafa", + "primary": "#fafafa", + "primaryForeground": "#171717", + "secondary": "#262626", + "secondaryForeground": "#fafafa", + "muted": "#262626", + "mutedForeground": "#a3a3a3", + "accent": "#262626", + "accentForeground": "#fafafa", + "destructive": "#7f1d1d", + "destructiveForeground": "#fafafa", + "border": "#262626", + "input": "#262626", + "ring": "#d4d4d4" + }, + "light": { + "background": "#ffffff", + "foreground": "#0a0a0a", + "card": "#ffffff", + "cardForeground": "#0a0a0a", + "popover": "#ffffff", + "popoverForeground": "#0a0a0a", + "primary": "#171717", + "primaryForeground": "#fafafa", + "secondary": "#f5f5f5", + "secondaryForeground": "#171717", + "muted": "#f5f5f5", + "mutedForeground": "#737373", + "accent": "#f5f5f5", + "accentForeground": "#171717", + "destructive": "#c92f32", + "destructiveForeground": "#fafafa", + "border": "#e5e5e5", + "input": "#e5e5e5", + "ring": "#0a0a0a" + } + }, + "motion": { + "duration": { + "fast": 100, + "base": 200, + "slow": 300 + } + }, + "radius": { + "none": 0, + "sm": 4, + "md": 8, + "lg": 12, + "full": 9999 + }, + "spacing": { + "1": 4, + "2": 8, + "3": 12, + "4": 16, + "6": 24, + "8": 32, + "12": 48, + "16": 64 + }, + "typography": { + "fontWeight": { + "heading": 600, + "display": 600, + "body": 400, + "caption": 500 + }, + "scale": { + "display": { + "fontSize": 60, + "lineHeight": 63 + }, + "h1": { + "fontSize": 48, + "lineHeight": 52.8 + }, + "h2": { + "fontSize": 36, + "lineHeight": 43.2 + }, + "h3": { + "fontSize": 30, + "lineHeight": 37.5 + }, + "h4": { + "fontSize": 24, + "lineHeight": 31.2 + }, + "h5": { + "fontSize": 20, + "lineHeight": 28 + }, + "h6": { + "fontSize": 18, + "lineHeight": 27 + }, + "bodyLarge": { + "fontSize": 18, + "lineHeight": 30.6 + }, + "body": { + "fontSize": 16, + "lineHeight": 25.6 + }, + "bodySmall": { + "fontSize": 14, + "lineHeight": 21 + }, + "caption": { + "fontSize": 12, + "lineHeight": 16.8 + } + } + } +} as const; + +export type BadgeVariant = + (typeof componentContracts.components.badge.variants)[number]; +export type ButtonSize = + (typeof componentContracts.components.button.sizes)[number]; +export type ButtonVariant = + (typeof componentContracts.components.button.variants)[number]; +export type CardPart = + (typeof componentContracts.components.card.parts)[number]; +export type HeadingLevel = + (typeof componentContracts.components.heading.levels)[number]; +export type TextSize = + (typeof componentContracts.components.text.sizes)[number]; +export type TextTone = + (typeof componentContracts.components.text.tones)[number]; +export type TextWeight = + (typeof componentContracts.components.text.weights)[number]; +export type SemanticColorName = keyof typeof nativeTokens.color.light; diff --git a/packages/ui-core/src/index.ts b/packages/ui-core/src/index.ts new file mode 100644 index 00000000..ba058eaf --- /dev/null +++ b/packages/ui-core/src/index.ts @@ -0,0 +1,27 @@ +export { + type BadgeVariant, + type ButtonSize, + type ButtonVariant, + type CardPart, + componentContracts, + designTokens, + type HeadingLevel, + nativeTokens, + type SemanticColorName, + type TextSize, + type TextTone, + type TextWeight, +} from "./generated/design-tokens"; +export { + type ComponentPlatform, + componentPlatforms, + isComponentPlatform, +} from "./platform"; +export { + createNativeTheme, + darkTheme, + lightTheme, + type NativeTheme, + type NativeThemeOverride, + type ThemeColorScheme, +} from "./theme"; diff --git a/packages/ui-core/src/platform.ts b/packages/ui-core/src/platform.ts new file mode 100644 index 00000000..6fb6030f --- /dev/null +++ b/packages/ui-core/src/platform.ts @@ -0,0 +1,12 @@ +/** Renderers represented in VLLNT UI registry metadata. */ +export const componentPlatforms = ["web", "native"] as const; + +/** A renderer target represented in VLLNT UI registry metadata. */ +export type ComponentPlatform = (typeof componentPlatforms)[number]; + +/** Returns whether an unknown value is a supported renderer target. */ +export function isComponentPlatform( + value: unknown, +): value is ComponentPlatform { + return value === "web" || value === "native"; +} diff --git a/packages/ui-core/src/theme.test.ts b/packages/ui-core/src/theme.test.ts new file mode 100644 index 00000000..29e0a3eb --- /dev/null +++ b/packages/ui-core/src/theme.test.ts @@ -0,0 +1,101 @@ +import { + componentContracts, + createNativeTheme, + darkTheme, + lightTheme, + nativeTokens, +} from "./index"; + +function relativeLuminance(hex: string): number { + const channels = [1, 3, 5] + .map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16) / 255) + .map((channel) => + channel <= 0.040_45 + ? channel / 12.92 + : ((channel + 0.055) / 1.055) ** 2.4, + ); + const red = channels[0] ?? 0; + const green = channels[1] ?? 0; + const blue = channels[2] ?? 0; + return 0.2126 * red + 0.7152 * green + 0.0722 * blue; +} + +function contrastRatio(first: string, second: string): number { + const light = Math.max(relativeLuminance(first), relativeLuminance(second)); + const dark = Math.min(relativeLuminance(first), relativeLuminance(second)); + return (light + 0.05) / (dark + 0.05); +} + +describe("generated design contracts", () => { + it("emits native-safe colors for both schemes", () => { + const themes = [lightTheme, darkTheme]; + expect( + themes.every((theme) => Object.keys(theme.colors).length === 19), + ).toBe(true); + expect( + themes + .flatMap((theme) => Object.values(theme.colors)) + .every((color) => /^#[\da-f]{6}$/.test(color)), + ).toBe(true); + expect(darkTheme.colors.background).not.toBe("#000000"); + }); + + it("keeps native control labels at WCAG AA contrast", () => { + expect( + [lightTheme, darkTheme].every( + (theme) => + contrastRatio( + theme.colors.destructive, + theme.colors.destructiveForeground, + ) >= 4.5, + ), + ).toBe(true); + expect( + [lightTheme, darkTheme].every( + (theme) => + contrastRatio(theme.colors.primary, theme.colors.primaryForeground) >= + 4.5, + ), + ).toBe(true); + }); + + it("converts shared dimensions to native points", () => { + expect(nativeTokens.spacing[1]).toBe(4); + expect(nativeTokens.spacing[4]).toBe(16); + expect(nativeTokens.spacing[16]).toBe(64); + expect(nativeTokens.radius).toMatchObject({ full: 9999, md: 8 }); + expect(nativeTokens.typography.scale.body).toEqual({ + fontSize: 16, + lineHeight: 25.6, + }); + }); + + it("keeps the portable pilot contract explicit", () => { + expect(componentContracts.components.button.variants).toEqual([ + "default", + "destructive", + "ghost", + "link", + "outline", + "secondary", + ]); + expect(componentContracts.components.heading.levels).toEqual([ + 1, 2, 3, 4, 5, 6, + ]); + }); +}); + +describe("createNativeTheme", () => { + it("merges semantic overrides without dropping defaults", () => { + const theme = createNativeTheme("dark", { + colors: { primary: "#123456" }, + spacing: { [4]: 20 }, + }); + + expect(theme.colorScheme).toBe("dark"); + expect(theme.colors.primary).toBe("#123456"); + expect(theme.colors.background).toBe(darkTheme.colors.background); + expect(theme.spacing[4]).toBe(20); + expect(theme.spacing[2]).toBe(darkTheme.spacing[2]); + }); +}); diff --git a/packages/ui-core/src/theme.ts b/packages/ui-core/src/theme.ts new file mode 100644 index 00000000..46d59e90 --- /dev/null +++ b/packages/ui-core/src/theme.ts @@ -0,0 +1,96 @@ +import { + nativeTokens, + type SemanticColorName, +} from "./generated/design-tokens"; + +/** Color modes available to renderer adapters. */ +export type ThemeColorScheme = keyof typeof nativeTokens.color; + +type NumberMap = { + readonly [Key in keyof T]: number; +}; + +type NativeFontWeight = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; + +/** Platform-neutral, React Native-compatible theme contract. */ +export type NativeTheme = { + readonly colors: Readonly>; + readonly colorScheme: ThemeColorScheme; + readonly motion: { + readonly duration: NumberMap; + }; + readonly radius: NumberMap; + readonly spacing: NumberMap; + readonly typography: { + readonly fontWeight: { + readonly [Key in keyof typeof nativeTokens.typography.fontWeight]: NativeFontWeight; + }; + readonly scale: { + readonly [Key in keyof typeof nativeTokens.typography.scale]: { + readonly fontSize: number; + readonly lineHeight: number; + }; + }; + }; +}; + +/** Supported semantic overrides for a generated native theme. */ +export type NativeThemeOverride = { + readonly colors?: Partial; + readonly motion?: { + readonly duration?: Partial; + }; + readonly radius?: Partial; + readonly spacing?: Partial; + readonly typography?: { + readonly fontWeight?: Partial; + readonly scale?: Partial; + }; +}; + +/** Generated light theme. Values are sRGB colors and density-independent points. */ +export const lightTheme: NativeTheme = { + colors: nativeTokens.color.light, + colorScheme: "light", + motion: nativeTokens.motion, + radius: nativeTokens.radius, + spacing: nativeTokens.spacing, + typography: nativeTokens.typography, +}; + +/** Generated dark theme. Values are sRGB colors and density-independent points. */ +export const darkTheme: NativeTheme = { + ...lightTheme, + colors: nativeTokens.color.dark, + colorScheme: "dark", +}; + +/** Creates a native theme while preserving every required semantic token. */ +export function createNativeTheme( + colorScheme: ThemeColorScheme, + override: NativeThemeOverride = {}, +): NativeTheme { + const base = colorScheme === "dark" ? darkTheme : lightTheme; + return { + colors: { ...base.colors, ...override.colors }, + colorScheme, + motion: { + duration: { + ...base.motion.duration, + ...override.motion?.duration, + }, + }, + radius: { ...base.radius, ...override.radius }, + spacing: { ...base.spacing, ...override.spacing }, + typography: { + fontWeight: { + ...base.typography.fontWeight, + ...override.typography?.fontWeight, + }, + scale: { + ...base.typography.scale, + ...override.typography?.scale, + }, + }, + }; +} diff --git a/packages/ui-core/tokens.json b/packages/ui-core/tokens.json new file mode 100644 index 00000000..83ad4021 --- /dev/null +++ b/packages/ui-core/tokens.json @@ -0,0 +1,278 @@ +{ + "$schema": "./tokens.schema.json", + "name": "VLLNT UI", + "version": "0.4.0", + "source": { + "guide": "../../DESIGN.md", + "theme": "../ui/themes/default.css" + }, + "color": { + "format": "oklch-channel", + "semantic": { + "background": { + "cssVariable": "--background", + "light": "1 0 0", + "dark": "0 0 0", + "role": "Page and app surface" + }, + "foreground": { + "cssVariable": "--foreground", + "light": "0.1445 0 0", + "dark": "0.9848 0 0", + "role": "Primary text and icon color" + }, + "card": { + "cssVariable": "--card", + "light": "1 0 0", + "dark": "0.1445 0 0", + "role": "Card and contained surface" + }, + "cardForeground": { + "cssVariable": "--card-foreground", + "light": "0.1445 0 0", + "dark": "0.9848 0 0", + "role": "Text on card surfaces" + }, + "popover": { + "cssVariable": "--popover", + "light": "1 0 0", + "dark": "0.1445 0 0", + "role": "Popover, menu, and floating surface" + }, + "popoverForeground": { + "cssVariable": "--popover-foreground", + "light": "0.1445 0 0", + "dark": "0.9848 0 0", + "role": "Text on popover surfaces" + }, + "primary": { + "cssVariable": "--primary", + "light": "0.2044 0 0", + "dark": "0.9848 0 0", + "role": "Primary action surface" + }, + "primaryForeground": { + "cssVariable": "--primary-foreground", + "light": "0.9848 0 0", + "dark": "0.2044 0 0", + "role": "Text on primary action surfaces" + }, + "secondary": { + "cssVariable": "--secondary", + "light": "0.9703 0 0", + "dark": "0.2686 0 0", + "role": "Secondary action surface" + }, + "secondaryForeground": { + "cssVariable": "--secondary-foreground", + "light": "0.2044 0 0", + "dark": "0.9848 0 0", + "role": "Text on secondary action surfaces" + }, + "muted": { + "cssVariable": "--muted", + "light": "0.9703 0 0", + "dark": "0.2686 0 0", + "role": "Subtle surface" + }, + "mutedForeground": { + "cssVariable": "--muted-foreground", + "light": "0.5555 0 0", + "dark": "0.7153 0 0", + "role": "Secondary text" + }, + "accent": { + "cssVariable": "--accent", + "light": "0.9703 0 0", + "dark": "0.2686 0 0", + "role": "Hover and active surface" + }, + "accentForeground": { + "cssVariable": "--accent-foreground", + "light": "0.2044 0 0", + "dark": "0.9848 0 0", + "role": "Text on accent surfaces" + }, + "destructive": { + "cssVariable": "--destructive", + "light": "0.6368 0.2078 25.326", + "dark": "0.3959 0.1331 25.721", + "role": "Destructive action and error surface" + }, + "destructiveForeground": { + "cssVariable": "--destructive-foreground", + "light": "0.9848 0 0", + "dark": "0.9848 0 0", + "role": "Text on destructive surfaces" + }, + "border": { + "cssVariable": "--border", + "light": "0.9219 0 0", + "dark": "0.2686 0 0", + "role": "Hairline divider" + }, + "input": { + "cssVariable": "--input", + "light": "0.9219 0 0", + "dark": "0.2686 0 0", + "role": "Input border" + }, + "ring": { + "cssVariable": "--ring", + "light": "0.1445 0 0", + "dark": "0.8697 0 0", + "role": "Focus ring" + } + } + }, + "typography": { + "fontFamily": { + "sans": { + "cssVariable": "--font-sans", + "value": "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif", + "role": "Body + UI text" + }, + "display": { + "cssVariable": "--font-display", + "value": "var(--font-sans)", + "role": "Heading + Display face — override per theme for a brand type identity (defaults to sans)" + }, + "mono": { + "cssVariable": "--font-mono", + "value": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", + "role": "Code + tabular" + } + }, + "fontWeight": { + "heading": { + "cssVariable": "--font-weight-heading", + "value": 600, + "role": "Heading primitive weight — theme-overridable" + }, + "display": { + "cssVariable": "--font-weight-display", + "value": 600, + "role": "Display primitive weight — theme-overridable" + }, + "body": { "value": 400 }, + "caption": { "value": 500 } + }, + "scale": { + "display": { + "cssVariable": "--font-size-display", + "fontSize": "3.75rem", + "lineHeight": "1.05", + "lineHeightCssVariable": "--line-height-display" + }, + "h1": { + "cssVariable": "--font-size-h1", + "fontSize": "3rem", + "lineHeight": "1.1", + "lineHeightCssVariable": "--line-height-h1" + }, + "h2": { + "cssVariable": "--font-size-h2", + "fontSize": "2.25rem", + "lineHeight": "1.2", + "lineHeightCssVariable": "--line-height-h2" + }, + "h3": { + "cssVariable": "--font-size-h3", + "fontSize": "1.875rem", + "lineHeight": "1.25", + "lineHeightCssVariable": "--line-height-h3" + }, + "h4": { + "cssVariable": "--font-size-h4", + "fontSize": "1.5rem", + "lineHeight": "1.3", + "lineHeightCssVariable": "--line-height-h4" + }, + "h5": { + "cssVariable": "--font-size-h5", + "fontSize": "1.25rem", + "lineHeight": "1.4", + "lineHeightCssVariable": "--line-height-h5" + }, + "h6": { + "cssVariable": "--font-size-h6", + "fontSize": "1.125rem", + "lineHeight": "1.5", + "lineHeightCssVariable": "--line-height-h6" + }, + "bodyLarge": { + "cssVariable": "--font-size-body-lg", + "fontSize": "1.125rem", + "lineHeight": "1.7", + "lineHeightCssVariable": "--line-height-body-lg" + }, + "body": { + "cssVariable": "--font-size-body", + "fontSize": "1rem", + "lineHeight": "1.6", + "lineHeightCssVariable": "--line-height-body" + }, + "bodySmall": { + "cssVariable": "--font-size-body-sm", + "fontSize": "0.875rem", + "lineHeight": "1.5", + "lineHeightCssVariable": "--line-height-body-sm" + }, + "caption": { + "cssVariable": "--font-size-caption", + "fontSize": "0.75rem", + "lineHeight": "1.4", + "lineHeightCssVariable": "--line-height-caption" + } + } + }, + "spacing": { + "unit": "4px", + "scale": { + "1": "0.25rem", + "2": "0.5rem", + "3": "0.75rem", + "4": "1rem", + "6": "1.5rem", + "8": "2rem", + "12": "3rem", + "16": "4rem" + } + }, + "radius": { + "none": "0", + "sm": "0.25rem", + "md": "0.5rem", + "lg": "0.75rem", + "full": "9999px" + }, + "elevation": { + "none": "none", + "sm": "0 1px 2px rgba(0, 0, 0, 0.05)", + "md": "0 4px 6px -1px rgba(0, 0, 0, 0.1)", + "lg": "0 10px 15px -3px rgba(0, 0, 0, 0.1)" + }, + "motion": { + "duration": { + "fast": "100ms", + "base": "200ms", + "slow": "300ms" + }, + "easing": { + "enter": "ease-out", + "exit": "ease-in", + "layout": "ease-in-out" + }, + "reducedMotion": "Collapse durations to 1ms or skip non-essential motion." + }, + "iconography": { + "library": "lucide-react", + "size": { + "default": "1rem", + "compact": "0.875rem", + "large": "1.25rem" + }, + "strokeWidth": 2, + "color": "currentColor" + } +} diff --git a/packages/ui-core/tokens.schema.json b/packages/ui-core/tokens.schema.json new file mode 100644 index 00000000..c56d81c3 --- /dev/null +++ b/packages/ui-core/tokens.schema.json @@ -0,0 +1,162 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "VLLNT UI design tokens", + "type": "object", + "required": [ + "name", + "version", + "source", + "color", + "typography", + "spacing", + "radius", + "elevation", + "motion", + "iconography" + ], + "properties": { + "$schema": { "type": "string" }, + "name": { "const": "VLLNT UI" }, + "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" }, + "source": { + "type": "object", + "required": ["guide", "theme"], + "properties": { + "guide": { "type": "string" }, + "theme": { "type": "string" } + }, + "additionalProperties": false + }, + "color": { + "type": "object", + "required": ["format", "semantic"], + "properties": { + "format": { "const": "oklch-channel" }, + "semantic": { + "type": "object", + "minProperties": 1, + "additionalProperties": { "$ref": "#/$defs/semanticColor" } + } + }, + "additionalProperties": false + }, + "typography": { + "type": "object", + "required": ["fontFamily", "fontWeight", "scale"], + "properties": { + "fontFamily": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/fontFamily" } + }, + "fontWeight": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/fontWeight" } + }, + "scale": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/typeScale" } + } + }, + "additionalProperties": false + }, + "spacing": { + "type": "object", + "required": ["unit", "scale"], + "properties": { + "unit": { "$ref": "#/$defs/cssLength" }, + "scale": { "$ref": "#/$defs/stringMap" } + }, + "additionalProperties": false + }, + "radius": { "$ref": "#/$defs/stringMap" }, + "elevation": { "$ref": "#/$defs/stringMap" }, + "motion": { + "type": "object", + "required": ["duration", "easing", "reducedMotion"], + "properties": { + "duration": { "$ref": "#/$defs/stringMap" }, + "easing": { "$ref": "#/$defs/stringMap" }, + "reducedMotion": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "iconography": { + "type": "object", + "required": ["library", "size", "strokeWidth", "color"], + "properties": { + "library": { "type": "string", "minLength": 1 }, + "size": { "$ref": "#/$defs/stringMap" }, + "strokeWidth": { "type": "number", "exclusiveMinimum": 0 }, + "color": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + } + }, + "$defs": { + "cssLength": { + "type": "string", + "pattern": "^-?[0-9]+(?:\\.[0-9]+)?(?:px|rem|em|%)$" + }, + "cssVariable": { + "type": "string", + "pattern": "^--[a-z][a-z0-9-]*$" + }, + "fontFamily": { + "type": "object", + "required": ["cssVariable", "value", "role"], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "value": { "type": "string", "minLength": 1 }, + "role": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "fontWeight": { + "type": "object", + "required": ["value"], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "value": { "type": "integer", "minimum": 1, "maximum": 1000 }, + "role": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "oklchChannel": { + "type": "string", + "pattern": "^(?:0(?:\\.[0-9]+)?|1(?:\\.0+)?) (?:0(?:\\.[0-9]+)?|1(?:\\.0+)?) -?[0-9]+(?:\\.[0-9]+)?$" + }, + "semanticColor": { + "type": "object", + "required": ["cssVariable", "light", "dark", "role"], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "light": { "$ref": "#/$defs/oklchChannel" }, + "dark": { "$ref": "#/$defs/oklchChannel" }, + "role": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "stringMap": { + "type": "object", + "minProperties": 1, + "additionalProperties": { "type": "string", "minLength": 1 } + }, + "typeScale": { + "type": "object", + "required": [ + "cssVariable", + "fontSize", + "lineHeight", + "lineHeightCssVariable" + ], + "properties": { + "cssVariable": { "$ref": "#/$defs/cssVariable" }, + "fontSize": { "$ref": "#/$defs/cssLength" }, + "lineHeight": { "type": "string", "pattern": "^[0-9]+(?:\\.[0-9]+)?$" }, + "lineHeightCssVariable": { "$ref": "#/$defs/cssVariable" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/packages/ui-core/tsconfig.build.json b/packages/ui-core/tsconfig.build.json new file mode 100644 index 00000000..b10f9535 --- /dev/null +++ b/packages/ui-core/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "incremental": false, + "outDir": "./dist", + "rootDir": "./src", + "types": [] + }, + "include": ["src"], + "exclude": ["node_modules", "src/**/*.test.ts"] +} diff --git a/packages/ui-core/tsconfig.json b/packages/ui-core/tsconfig.json new file mode 100644 index 00000000..c2433bbb --- /dev/null +++ b/packages/ui-core/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@vllnt/typescript/base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "lib": ["ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2020", + "types": ["vitest/globals"] + }, + "include": ["src", "tsup.config.ts", "vitest.config.ts"] +} diff --git a/packages/ui-core/tsup.config.ts b/packages/ui-core/tsup.config.ts new file mode 100644 index 00000000..c1f77c0d --- /dev/null +++ b/packages/ui-core/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + bundle: true, + clean: true, + dts: true, + entry: ["src/index.ts"], + format: ["esm"], + outDir: "dist", + target: "es2020", + tsconfig: "tsconfig.build.json", +}); diff --git a/packages/ui-core/vitest.config.ts b/packages/ui-core/vitest.config.ts new file mode 100644 index 00000000..869d8d7e --- /dev/null +++ b/packages/ui-core/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + globals: true, + include: ["src/**/*.test.ts"], + }, +}); diff --git a/packages/ui-native/CHANGELOG.md b/packages/ui-native/CHANGELOG.md new file mode 100644 index 00000000..6ecf525a --- /dev/null +++ b/packages/ui-native/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to `@vllnt/ui-native` are documented in this file. + +## [Unreleased] + +### Added + +- Experimental React Native renderer with Button, Text, Heading, Badge, Card, and shared light/dark theme support. diff --git a/packages/ui-native/README.md b/packages/ui-native/README.md new file mode 100644 index 00000000..d1588fec --- /dev/null +++ b/packages/ui-native/README.md @@ -0,0 +1,36 @@ +# @vllnt/ui-native + +Accessible React Native components using the same semantic tokens and portable option contracts as `@vllnt/ui`, with a renderer designed specifically for React Native. + +> Experimental. Install from the `canary` tag. Stable publishing is intentionally disabled while the API is validated in real Expo applications. + +## Install + +```bash +pnpm add @vllnt/ui-native@canary +``` + +React 19 and React Native 0.81 or newer are required peer dependencies. + +## Use + +```tsx +import { Button, Card, CardContent, Text, ThemeProvider } from "@vllnt/ui-native"; + +export function Example() { + return ( + + + + Native VLLNT UI + + + + + ); +} +``` + +The pilot includes `Button`, `Text`, `Heading`, `Badge`, and the compound `Card` family. `Button` accepts a string or numeric label so every variant can apply its accessible foreground color; richer icon/content composition is intentionally deferred. The renderer uses React Native primitives and `StyleSheet`; it does not depend on the DOM renderer, Radix UI, Tailwind CSS, NativeWind, or web globals. + +The `ThemeProvider` follows the device color scheme by default. Pass `colorScheme="light"` or `colorScheme="dark"` for a fixed mode, and `override` for semantic token customization. diff --git a/packages/ui-native/babel.config.cjs b/packages/ui-native/babel.config.cjs new file mode 100644 index 00000000..3c25e5d9 --- /dev/null +++ b/packages/ui-native/babel.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + presets: ["module:@react-native/babel-preset"], +}; diff --git a/packages/ui-native/eslint.config.js b/packages/ui-native/eslint.config.js new file mode 100644 index 00000000..1eccb39d --- /dev/null +++ b/packages/ui-native/eslint.config.js @@ -0,0 +1,31 @@ +import { react } from "@vllnt/eslint-config"; + +export default [ + { + ignores: [ + "dist/**", + "node_modules/**", + "scripts/**", + "eslint.config.js", + "babel.config.cjs", + "jest.config.cjs", + "tsup.config.ts", + ], + }, + ...react, + { + linterOptions: { + reportUnusedDisableDirectives: "error", + }, + rules: { + "@next/next/no-html-link-for-pages": "off", + "jsx-a11y/label-has-associated-control": "off", + }, + }, + { + files: ["**/*.test.{ts,tsx}"], + rules: { + "max-lines-per-function": "off", + }, + }, +]; diff --git a/packages/ui-native/jest.config.cjs b/packages/ui-native/jest.config.cjs new file mode 100644 index 00000000..919becde --- /dev/null +++ b/packages/ui-native/jest.config.cjs @@ -0,0 +1,11 @@ +/** @type {import('jest').Config} */ +module.exports = { + preset: "react-native", + moduleNameMapper: { + "^@vllnt/ui-core$": "/../ui-core/src/index.ts", + }, + testMatch: ["/src/**/*.test.ts", "/src/**/*.test.tsx"], + transformIgnorePatterns: [ + "node_modules/(?!((?:\\.pnpm/[^/]+/node_modules/)?(?:react-native|@react-native(?:-community)?|@testing-library/react-native))/)", + ], +}; diff --git a/packages/ui-native/package.json b/packages/ui-native/package.json new file mode 100644 index 00000000..df903910 --- /dev/null +++ b/packages/ui-native/package.json @@ -0,0 +1,91 @@ +{ + "name": "@vllnt/ui-native", + "version": "0.1.0", + "description": "Accessible React Native renderer for VLLNT UI", + "license": "MIT", + "author": "vllnt", + "homepage": "https://ui.vllnt.com/docs/native", + "repository": { + "type": "git", + "url": "git+https://github.com/vllnt/ui.git", + "directory": "packages/ui-native" + }, + "bugs": { + "url": "https://github.com/vllnt/ui/issues" + }, + "keywords": ["react-native", "expo", "components", "design-system"], + "type": "module", + "main": "./src/index.ts", + "module": "./src/index.ts", + "types": "./src/index.ts", + "react-native": "./dist/index.js", + "exports": { + ".": { + "types": "./src/index.ts", + "react-native": "./src/index.ts", + "import": "./src/index.ts", + "default": "./src/index.ts" + }, + "./registry.json": "./registry.json" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org", + "access": "public", + "tag": "canary", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "react-native": "./dist/index.js", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "react-native": "./dist/index.js", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./registry.json": "./registry.json" + } + }, + "files": [ + "dist", + "registry.json", + "registry.schema.json", + "CHANGELOG.md", + "README.md" + ], + "sideEffects": false, + "scripts": { + "boundaries:check": "node scripts/check-boundaries.mjs", + "build": "tsup", + "clean": "rm -rf dist node_modules coverage", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "pack:check": "node scripts/check-packed-package.mjs", + "test": "jest --watch", + "test:once": "jest --runInBand", + "typecheck": "tsc --noEmit --project tsconfig.json" + }, + "peerDependencies": { + "react": ">=19.0.0 <20", + "react-native": ">=0.81.0 <1" + }, + "dependencies": { + "@vllnt/ui-core": "workspace:*" + }, + "devDependencies": { + "@react-native/babel-preset": "0.86.3", + "@testing-library/react-native": "^13.3.3", + "@types/jest": "^29.5.14", + "@types/react": "19.2.13", + "@vllnt/eslint-config": "^1.0.0", + "@vllnt/typescript": "^1.0.0", + "babel-jest": "^29.7.0", + "eslint": "^9.39.1", + "jest": "^29.7.0", + "react": "19.2.3", + "react-native": "0.86.3", + "react-test-renderer": "19.2.3", + "tsup": "^8.5.0", + "typescript": "^5.9.3" + } +} diff --git a/packages/ui-native/registry.json b/packages/ui-native/registry.json new file mode 100644 index 00000000..e0d49c08 --- /dev/null +++ b/packages/ui-native/registry.json @@ -0,0 +1,13 @@ +{ + "$schema": "./registry.schema.json", + "package": "@vllnt/ui-native", + "channel": "canary", + "status": "experimental", + "components": [ + { "name": "badge", "parity": "full" }, + { "name": "button", "parity": "full" }, + { "name": "card", "parity": "full" }, + { "name": "heading", "parity": "full" }, + { "name": "text", "parity": "full" } + ] +} diff --git a/packages/ui-native/registry.schema.json b/packages/ui-native/registry.schema.json new file mode 100644 index 00000000..35b037ba --- /dev/null +++ b/packages/ui-native/registry.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "VLLNT UI native catalog", + "type": "object", + "required": ["package", "channel", "status", "components"], + "properties": { + "$schema": { "type": "string" }, + "package": { "const": "@vllnt/ui-native" }, + "channel": { "const": "canary" }, + "status": { "const": "experimental" }, + "components": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "parity"], + "properties": { + "name": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" }, + "parity": { "enum": ["full", "api-only"] } + }, + "additionalProperties": false + }, + "minItems": 1, + "uniqueItems": true + } + }, + "additionalProperties": false +} diff --git a/packages/ui-native/scripts/check-boundaries.mjs b/packages/ui-native/scripts/check-boundaries.mjs new file mode 100644 index 00000000..2e371d31 --- /dev/null +++ b/packages/ui-native/scripts/check-boundaries.mjs @@ -0,0 +1,44 @@ +import { readdir, readFile } from "node:fs/promises"; +import { extname, join, relative } from "node:path"; +import { fileURLToPath } from "node:url"; + +const sourceRoot = fileURLToPath(new URL("../src/", import.meta.url)); +const bannedImports = [ + "@radix-ui/", + "@vllnt/ui\"", + "@vllnt/ui'", + "nativewind", + "next/", + "react-dom", + "tailwindcss", +]; + +async function sourceFiles(directory) { + const entries = await readdir(directory, { withFileTypes: true }); + const nested = await Promise.all( + entries.map((entry) => { + const path = join(directory, entry.name); + return entry.isDirectory() ? sourceFiles(path) : [path]; + }), + ); + return nested.flat().filter((path) => [".ts", ".tsx"].includes(extname(path))); +} + +const errors = []; +for (const path of await sourceFiles(sourceRoot)) { + if (path.endsWith(".test.ts") || path.endsWith(".test.tsx")) continue; + const source = await readFile(path, "utf8"); + for (const bannedImport of bannedImports) { + if (source.includes(bannedImport)) { + errors.push(`${relative(sourceRoot, path)} imports ${bannedImport}`); + } + } +} + +if (errors.length > 0) { + console.error("Native renderer boundary check failed:"); + for (const error of errors) console.error(` - ${error}`); + process.exit(1); +} + +console.log("Native renderer boundary check passed."); diff --git a/packages/ui-native/scripts/check-packed-package.mjs b/packages/ui-native/scripts/check-packed-package.mjs new file mode 100644 index 00000000..83f0760c --- /dev/null +++ b/packages/ui-native/scripts/check-packed-package.mjs @@ -0,0 +1,82 @@ +import { mkdtemp, readFile, rm, stat } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; + +const packageDirectory = resolve(import.meta.dirname, ".."); +const temporaryDirectory = await mkdtemp(join(tmpdir(), "vllnt-ui-native-pack-")); + +function run(command, arguments_, cwd = packageDirectory) { + const result = spawnSync(command, arguments_, { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "inherit"], + }); + if (result.status !== 0) { + throw new Error(`${command} ${arguments_.join(" ")} failed.`); + } + return result.stdout.trim(); +} + +async function assertFile(path, field) { + try { + const metadata = await stat(path); + if (!metadata.isFile()) throw new Error(); + } catch { + throw new Error(`Packed ${field} target is missing: ${path}`); + } +} + +try { + const output = run("pnpm", [ + "pack", + "--pack-destination", + temporaryDirectory, + ]); + const tarball = output.split("\n").at(-1); + if (!tarball?.endsWith(".tgz")) { + throw new Error(`Could not identify packed tarball from: ${output}`); + } + + run("tar", [ + "-xzf", + resolve(packageDirectory, tarball), + "-C", + temporaryDirectory, + ]); + const packedDirectory = join(temporaryDirectory, "package"); + const manifest = JSON.parse( + await readFile(join(packedDirectory, "package.json"), "utf8"), + ); + const entryFields = ["main", "module", "react-native", "types"]; + + for (const field of entryFields) { + const target = manifest[field]; + if (typeof target !== "string" || target.startsWith("./src/")) { + throw new Error(`Packed ${field} must target dist; received ${target}.`); + } + await assertFile(join(packedDirectory, target), field); + } + + const rootExport = manifest.exports?.["."]; + for (const condition of ["default", "import", "react-native", "types"]) { + const target = rootExport?.[condition]; + if (typeof target !== "string" || target.startsWith("./src/")) { + throw new Error( + `Packed exports[\".\"].${condition} must target dist; received ${target}.`, + ); + } + await assertFile(join(packedDirectory, target), `exports.${condition}`); + } + + const coreRange = manifest.dependencies?.["@vllnt/ui-core"]; + if (typeof coreRange !== "string" || coreRange.startsWith("workspace:")) { + throw new Error(`Packed @vllnt/ui-core range is invalid: ${coreRange}.`); + } + + console.log( + `Packed native package resolves main, module, react-native, and types from dist (${manifest.version}).`, + ); +} finally { + await rm(temporaryDirectory, { force: true, recursive: true }); +} diff --git a/packages/ui-native/src/components/badge/badge.tsx b/packages/ui-native/src/components/badge/badge.tsx new file mode 100644 index 00000000..1f364cea --- /dev/null +++ b/packages/ui-native/src/components/badge/badge.tsx @@ -0,0 +1,117 @@ +import type { BadgeVariant, NativeTheme } from "@vllnt/ui-core"; +import type { ReactNode, Ref } from "react"; +import { + type StyleProp, + StyleSheet, + Text as NativeText, + type TextStyle, + View, + type ViewProps, + type ViewStyle, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Props for the React Native Badge renderer. */ +export type BadgeProps = ViewProps & { + readonly children: ReactNode; + readonly ref?: Ref; + readonly textStyle?: StyleProp; + readonly variant?: BadgeVariant; +}; + +type BadgeResolvedStyles = { + readonly container: ViewStyle; + readonly text: TextStyle; +}; + +const styles = StyleSheet.create({ + base: { + alignItems: "center", + alignSelf: "flex-start", + borderWidth: 1, + justifyContent: "center", + minHeight: 24, + }, +}); + +function resolveBadgeStyles( + theme: NativeTheme, + variant: BadgeVariant, +): BadgeResolvedStyles { + const variants: Record = { + default: { + container: { + backgroundColor: theme.colors.primary, + borderColor: theme.colors.primary, + }, + text: { color: theme.colors.primaryForeground }, + }, + destructive: { + container: { + backgroundColor: theme.colors.destructive, + borderColor: theme.colors.destructive, + }, + text: { color: theme.colors.destructiveForeground }, + }, + outline: { + container: { + backgroundColor: "transparent", + borderColor: theme.colors.border, + }, + text: { color: theme.colors.foreground }, + }, + secondary: { + container: { + backgroundColor: theme.colors.secondary, + borderColor: theme.colors.secondary, + }, + text: { color: theme.colors.secondaryForeground }, + }, + }; + return variants[variant]; +} + +/** Compact status label using shared semantic Badge variants. */ +function Badge({ + children, + ref, + style, + textStyle, + variant = "default", + ...props +}: BadgeProps) { + const theme = useTheme(); + const resolved = resolveBadgeStyles(theme, variant); + + return ( + + + {children} + + + ); +} +Badge.displayName = "Badge"; + +export { Badge }; diff --git a/packages/ui-native/src/components/button/button-styles.ts b/packages/ui-native/src/components/button/button-styles.ts new file mode 100644 index 00000000..e27e3b4e --- /dev/null +++ b/packages/ui-native/src/components/button/button-styles.ts @@ -0,0 +1,98 @@ +import type { ButtonSize, ButtonVariant, NativeTheme } from "@vllnt/ui-core"; +import type { TextStyle, ViewStyle } from "react-native"; + +export type ButtonResolvedStyles = { + readonly container: ViewStyle; + readonly text: TextStyle; +}; + +function resolveButtonSizeStyle( + theme: NativeTheme, + size: ButtonSize, +): ViewStyle { + const sizes: Record = { + default: { + minHeight: 44, + paddingHorizontal: theme.spacing[4], + paddingVertical: theme.spacing[2], + }, + icon: { height: 44, paddingHorizontal: theme.spacing[2], width: 44 }, + lg: { + minHeight: 48, + paddingHorizontal: theme.spacing[8], + paddingVertical: theme.spacing[3], + }, + sm: { + minHeight: 44, + paddingHorizontal: theme.spacing[3], + paddingVertical: theme.spacing[2], + }, + }; + return sizes[size]; +} + +function solidStyles( + backgroundColor: string, + color: string, +): ButtonResolvedStyles { + return { container: { backgroundColor }, text: { color } }; +} + +function transparentStyles(color: string): ButtonResolvedStyles { + return { container: { backgroundColor: "transparent" }, text: { color } }; +} + +function linkStyles(theme: NativeTheme): ButtonResolvedStyles { + return { + ...transparentStyles(theme.colors.primary), + text: { color: theme.colors.primary, textDecorationLine: "underline" }, + }; +} + +function outlineStyles(theme: NativeTheme): ButtonResolvedStyles { + return { + container: { + backgroundColor: theme.colors.background, + borderColor: theme.colors.input, + borderWidth: 1, + }, + text: { color: theme.colors.foreground }, + }; +} + +function resolveButtonVariantStyles( + theme: NativeTheme, + variant: ButtonVariant, +): ButtonResolvedStyles { + if (variant === "default") + return solidStyles(theme.colors.primary, theme.colors.primaryForeground); + if (variant === "destructive") + return solidStyles( + theme.colors.destructive, + theme.colors.destructiveForeground, + ); + if (variant === "secondary") + return solidStyles( + theme.colors.secondary, + theme.colors.secondaryForeground, + ); + if (variant === "ghost") return transparentStyles(theme.colors.foreground); + if (variant === "link") return linkStyles(theme); + return outlineStyles(theme); +} + +/** Resolves Button's shared variants into native semantic styles. */ +export function resolveButtonStyles( + theme: NativeTheme, + variant: ButtonVariant, + size: ButtonSize, +): ButtonResolvedStyles { + const variantStyles = resolveButtonVariantStyles(theme, variant); + return { + container: { + ...resolveButtonSizeStyle(theme, size), + ...variantStyles.container, + }, + text: variantStyles.text, + }; +} diff --git a/packages/ui-native/src/components/button/button.tsx b/packages/ui-native/src/components/button/button.tsx new file mode 100644 index 00000000..45b626a9 --- /dev/null +++ b/packages/ui-native/src/components/button/button.tsx @@ -0,0 +1,97 @@ +import type { ButtonSize, ButtonVariant } from "@vllnt/ui-core"; +import type { Ref } from "react"; +import { + Pressable, + type PressableProps, + type StyleProp, + StyleSheet, + Text as NativeText, + type TextStyle, + type View, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +import { resolveButtonStyles } from "./button-styles"; + +/** Props for the React Native Button renderer. */ +export type ButtonProps = Omit & { + /** Native text content styled for the selected semantic variant. */ + readonly children: number | string; + readonly ref?: Ref; + readonly size?: ButtonSize; + readonly textStyle?: StyleProp; + readonly variant?: ButtonVariant; +}; + +const styles = StyleSheet.create({ + base: { + alignItems: "center", + flexDirection: "row", + justifyContent: "center", + }, + disabled: { + opacity: 0.5, + }, + pressed: { + opacity: 0.8, + }, + text: { + textAlign: "center", + }, +}); + +/** Accessible native action with the same semantic variants as the web Button. */ +function Button({ + accessibilityLabel, + accessibilityState, + children, + disabled = false, + ref, + size = "default", + style, + textStyle, + variant = "default", + ...props +}: ButtonProps) { + const theme = useTheme(); + const isDisabled = disabled === true; + const resolved = resolveButtonStyles(theme, variant, size); + const content = ( + + {children} + + ); + + return ( + [ + styles.base, + { borderRadius: theme.radius.md, gap: theme.spacing[2] }, + resolved.container, + state.pressed ? styles.pressed : undefined, + isDisabled ? styles.disabled : undefined, + typeof style === "function" ? style(state) : style, + ]} + > + {content} + + ); +} +Button.displayName = "Button"; + +export { Button }; diff --git a/packages/ui-native/src/components/card/card.tsx b/packages/ui-native/src/components/card/card.tsx new file mode 100644 index 00000000..74ac5e78 --- /dev/null +++ b/packages/ui-native/src/components/card/card.tsx @@ -0,0 +1,158 @@ +import type { Ref } from "react"; +import { + StyleSheet, + Text as NativeText, + type Text as NativeTextInstance, + type TextProps, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +type NativeViewProps = ViewProps & { readonly ref?: Ref }; +type NativeCardTextProps = TextProps & { + readonly ref?: Ref; +}; + +/** Props shared by Card's native view regions. */ +export type CardProps = NativeViewProps; +/** Props for the native CardHeader region. */ +export type CardHeaderProps = NativeViewProps; +/** Props for the native CardContent region. */ +export type CardContentProps = NativeViewProps; +/** Props for the native CardFooter region. */ +export type CardFooterProps = NativeViewProps; +/** Props for native Card title text. */ +export type CardTitleProps = NativeCardTextProps; +/** Props for native Card description text. */ +export type CardDescriptionProps = NativeCardTextProps; + +const styles = StyleSheet.create({ + footer: { + alignItems: "center", + flexDirection: "row", + }, + root: { + borderWidth: 1, + }, +}); + +/** Token-driven native card surface. */ +function Card({ ref, style, ...props }: CardProps) { + const theme = useTheme(); + return ( + + ); +} +Card.displayName = "Card"; + +/** Top region for a native Card. */ +function CardHeader({ ref, style, ...props }: CardHeaderProps) { + const theme = useTheme(); + return ( + + ); +} +CardHeader.displayName = "CardHeader"; + +/** Heading text for a native Card. */ +function CardTitle({ ref, style, ...props }: CardTitleProps) { + const theme = useTheme(); + return ( + + ); +} +CardTitle.displayName = "CardTitle"; + +/** Supporting text for a native Card. */ +function CardDescription({ ref, style, ...props }: CardDescriptionProps) { + const theme = useTheme(); + return ( + + ); +} +CardDescription.displayName = "CardDescription"; + +/** Main content region for a native Card. */ +function CardContent({ ref, style, ...props }: CardContentProps) { + const theme = useTheme(); + return ( + + ); +} +CardContent.displayName = "CardContent"; + +/** Action region for a native Card. */ +function CardFooter({ ref, style, ...props }: CardFooterProps) { + const theme = useTheme(); + return ( + + ); +} +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +}; diff --git a/packages/ui-native/src/components/components.test.tsx b/packages/ui-native/src/components/components.test.tsx new file mode 100644 index 00000000..d3c0a0bc --- /dev/null +++ b/packages/ui-native/src/components/components.test.tsx @@ -0,0 +1,93 @@ +import { fireEvent, render, screen } from "@testing-library/react-native"; + +import { + Badge, + Button, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, + Heading, + Text, + ThemeProvider, +} from "../index"; + +describe("native pilot components", () => { + it("renders an accessible button and handles presses", () => { + const onPress = jest.fn(); + render( + + + , + ); + + const button = screen.getByRole("button", { name: "Save changes" }); + fireEvent.press(button); + + expect(onPress).toHaveBeenCalledTimes(1); + expect(button).toBeEnabled(); + }); + + it("exposes disabled button state without invoking the action", () => { + const onPress = jest.fn(); + render( + , + ); + + const button = screen.getByRole("button", { name: "Delete item" }); + fireEvent.press(button); + + expect(onPress).not.toHaveBeenCalled(); + expect(button).toBeDisabled(); + }); + + it("keeps heading semantics independent from visual size", () => { + render( + + + Account + + , + ); + + const heading = screen.getByRole("header", { name: "Account" }); + expect(heading).toHaveProp("aria-level", 2); + expect(heading).toHaveStyle({ fontSize: 48 }); + }); + + it("renders text, badge, and compound card regions", () => { + render( + + + + Experimental + Native renderer + + Shared tokens, separate primitives. + + + + Runs in Expo. + + + Canary only + + + , + ); + + expect(screen.getByTestId("card")).toBeOnTheScreen(); + expect(screen.getByText("Experimental")).toBeOnTheScreen(); + expect( + screen.getByRole("header", { name: "Native renderer" }), + ).toBeOnTheScreen(); + expect(screen.getByText("Runs in Expo.")).toBeOnTheScreen(); + expect(screen.getByText("Canary only")).toBeOnTheScreen(); + }); +}); diff --git a/packages/ui-native/src/components/heading/heading.tsx b/packages/ui-native/src/components/heading/heading.tsx new file mode 100644 index 00000000..1ddcbc26 --- /dev/null +++ b/packages/ui-native/src/components/heading/heading.tsx @@ -0,0 +1,52 @@ +import type { HeadingLevel } from "@vllnt/ui-core"; +import type { Ref } from "react"; +import { + Text as NativeText, + type Text as NativeTextInstance, + type TextProps as NativeTextProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Props for semantic React Native headings. */ +export type HeadingProps = Omit< + NativeTextProps, + "accessibilityRole" | "aria-level" +> & { + readonly level?: HeadingLevel; + readonly ref?: Ref; + readonly size?: HeadingLevel; +}; + +/** Native heading with semantic level and independently selectable visual size. */ +function Heading({ level = 2, ref, size, style, ...props }: HeadingProps) { + const theme = useTheme(); + const scale = { + 1: theme.typography.scale.h1, + 2: theme.typography.scale.h2, + 3: theme.typography.scale.h3, + 4: theme.typography.scale.h4, + 5: theme.typography.scale.h5, + 6: theme.typography.scale.h6, + } satisfies Record; + + return ( + + ); +} +Heading.displayName = "Heading"; + +export { Heading }; diff --git a/packages/ui-native/src/components/text/text.tsx b/packages/ui-native/src/components/text/text.tsx new file mode 100644 index 00000000..250128eb --- /dev/null +++ b/packages/ui-native/src/components/text/text.tsx @@ -0,0 +1,62 @@ +import type { TextSize, TextTone, TextWeight } from "@vllnt/ui-core"; +import type { Ref } from "react"; +import { + Text as NativeText, + type Text as NativeTextInstance, + type TextProps as NativeTextProps, + type TextStyle, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Props for token-driven React Native body text. */ +export type TextProps = NativeTextProps & { + readonly ref?: Ref; + readonly size?: TextSize; + readonly tone?: TextTone; + readonly weight?: TextWeight; +}; + +/** React Native body text backed by the shared typography contract. */ +function Text({ + ref, + size = "base", + style, + tone = "default", + weight = "normal", + ...props +}: TextProps) { + const theme = useTheme(); + const scale = { + base: theme.typography.scale.body, + caption: theme.typography.scale.caption, + lead: theme.typography.scale.bodyLarge, + small: theme.typography.scale.bodySmall, + } satisfies Record; + const fontWeight = { + medium: theme.typography.fontWeight.caption, + normal: theme.typography.fontWeight.body, + semibold: theme.typography.fontWeight.heading, + } satisfies Record; + + return ( + + ); +} +Text.displayName = "Text"; + +export { Text }; diff --git a/packages/ui-native/src/index.ts b/packages/ui-native/src/index.ts new file mode 100644 index 00000000..de86b839 --- /dev/null +++ b/packages/ui-native/src/index.ts @@ -0,0 +1,37 @@ +export { Badge, type BadgeProps } from "./components/badge/badge"; +export { Button, type ButtonProps } from "./components/button/button"; +export { + Card, + CardContent, + type CardContentProps, + CardDescription, + type CardDescriptionProps, + CardFooter, + type CardFooterProps, + CardHeader, + type CardHeaderProps, + type CardProps, + CardTitle, + type CardTitleProps, +} from "./components/card/card"; +export { Heading, type HeadingProps } from "./components/heading/heading"; +export { Text, type TextProps } from "./components/text/text"; +export { + ThemeProvider, + type ThemeProviderProps, + type ThemeSelection, + useTheme, +} from "./theme/theme-provider"; +export type { + BadgeVariant, + ButtonSize, + ButtonVariant, + HeadingLevel, + NativeTheme, + NativeThemeOverride, + TextSize, + TextTone, + TextWeight, + ThemeColorScheme, +} from "@vllnt/ui-core"; +export { darkTheme, lightTheme } from "@vllnt/ui-core"; diff --git a/packages/ui-native/src/theme/theme-provider.tsx b/packages/ui-native/src/theme/theme-provider.tsx new file mode 100644 index 00000000..2c2c4d40 --- /dev/null +++ b/packages/ui-native/src/theme/theme-provider.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { createContext, type ReactNode, use, useMemo } from "react"; + +import { + createNativeTheme, + lightTheme, + type NativeTheme, + type NativeThemeOverride, + type ThemeColorScheme, +} from "@vllnt/ui-core"; +import { useColorScheme } from "react-native"; + +const ThemeContext = createContext(lightTheme); + +/** Color selection accepted by {@link ThemeProvider}. */ +export type ThemeSelection = "system" | ThemeColorScheme; + +/** Props for the native VLLNT UI theme boundary. */ +export type ThemeProviderProps = { + readonly children: ReactNode; + readonly colorScheme?: ThemeSelection; + readonly override?: NativeThemeOverride; +}; + +/** Supplies generated semantic tokens to every native component below it. */ +function ThemeProvider({ + children, + colorScheme = "system", + override, +}: ThemeProviderProps) { + const systemColorScheme = useColorScheme(); + const resolvedColorScheme = + colorScheme === "system" + ? systemColorScheme === "dark" + ? "dark" + : "light" + : colorScheme; + const theme = useMemo( + () => createNativeTheme(resolvedColorScheme, override), + [override, resolvedColorScheme], + ); + + return {children}; +} +ThemeProvider.displayName = "ThemeProvider"; + +/** Reads the nearest VLLNT UI native theme, defaulting to the light theme. */ +function useTheme(): NativeTheme { + return use(ThemeContext); +} + +export { ThemeProvider, useTheme }; diff --git a/packages/ui-native/tsconfig.build.json b/packages/ui-native/tsconfig.build.json new file mode 100644 index 00000000..50ddc571 --- /dev/null +++ b/packages/ui-native/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "incremental": false, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"], + "exclude": ["node_modules", "src/**/*.test.ts", "src/**/*.test.tsx"] +} diff --git a/packages/ui-native/tsconfig.json b/packages/ui-native/tsconfig.json new file mode 100644 index 00000000..fecc40b8 --- /dev/null +++ b/packages/ui-native/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@vllnt/typescript/react.json", + "compilerOptions": { + "baseUrl": ".", + "incremental": false, + "lib": ["ES2022"], + "paths": { + "@/*": ["./src/*"] + }, + "target": "ES2020", + "types": ["jest", "react", "react-native"] + }, + "include": ["src", "tests", "jest.config.cjs", "tsup.config.ts"] +} diff --git a/packages/ui-native/tsup.config.ts b/packages/ui-native/tsup.config.ts new file mode 100644 index 00000000..e5ffc113 --- /dev/null +++ b/packages/ui-native/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + bundle: true, + clean: true, + dts: true, + entry: ["src/index.ts"], + external: ["@vllnt/ui-core", "react", "react-native"], + format: ["esm"], + outDir: "dist", + target: "es2020", + tsconfig: "tsconfig.build.json", +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88c41723..c4ac5002 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,15 +37,80 @@ overrides: lodash: '>=4.18.1' flatted: '>=3.4.2' postcss: '>=8.5.10' + ws@>=8.0.0 <8.21.0: 8.21.3 importers: .: devDependencies: + madge: + specifier: ^8.0.0 + version: 8.0.0(typescript@5.9.3) turbo: specifier: ^2.4.4 version: 2.8.3 + apps/native-catalog: + dependencies: + '@vllnt/ui-native': + specifier: workspace:* + version: link:../../packages/ui-native + expo: + specifier: ~57.0.19 + version: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + expo-status-bar: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + react: + specifier: 19.2.3 + version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + react-native: + specifier: 0.86.3 + version: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + react-native-web: + specifier: ^0.21.2 + version: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + devDependencies: + '@react-native/babel-preset': + specifier: 0.86.3 + version: 0.86.3(@babel/core@7.29.0) + '@testing-library/react-native': + specifier: ^13.3.3 + version: 13.3.3(jest@29.7.0(@types/node@22.19.10))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/react': + specifier: 19.2.13 + version: 19.2.13 + '@vllnt/eslint-config': + specifier: ^1.0.0 + version: 1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.3))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) + '@vllnt/typescript': + specifier: ^1.0.0 + version: 1.0.0 + babel-jest: + specifier: ^29.7.0 + version: 29.7.0(@babel/core@7.29.0) + eslint: + specifier: ^9.39.1 + version: 9.39.2(jiti@1.21.7) + expo-doctor: + specifier: ^1.20.4 + version: 1.20.4 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.10) + react-test-renderer: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + apps/registry: dependencies: '@codesandbox/sandpack-react': @@ -59,16 +124,19 @@ importers: version: 16.2.6(@mdx-js/react@3.1.1(@types/react@19.2.13)(react@19.2.4)) '@vercel/analytics': specifier: ^1.5.0 - version: 1.6.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + version: 1.6.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@vercel/speed-insights': specifier: ^1.2.0 - version: 1.3.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + version: 1.3.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@vllnt/next-llms': specifier: canary - version: 0.1.0-canary.78c9be3 + version: 0.1.0-canary.bfc9152 '@vllnt/ui': specifier: workspace:* version: link:../../packages/ui + '@vllnt/ui-core': + specifier: workspace:* + version: link:../../packages/ui-core class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -83,10 +151,10 @@ importers: version: 0.468.0(react@19.2.4) next: specifier: '>=16.2.6' - version: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-intl: specifier: ^4.11.2 - version: 4.13.0(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 4.13.0(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: specifier: ^19.2.0 version: 19.2.4 @@ -111,7 +179,7 @@ importers: version: 1.60.0 '@tailwindcss/typography': specifier: ^0.5.16 - version: 0.5.19(tailwindcss@3.4.19(tsx@4.21.0)) + version: 0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0)) '@types/mdx': specifier: ^2.0.13 version: 2.0.13 @@ -126,13 +194,13 @@ importers: version: 19.2.3(@types/react@19.2.13) '@vllnt/eslint-config': specifier: ^1.0.0 - version: 1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) + version: 1.0.0(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) '@vllnt/typescript': specifier: ^1.0.0 version: 1.0.0 autoprefixer: specifier: ^10.4.20 - version: 10.4.24(postcss@8.5.10) + version: 10.4.24(postcss@8.5.28) eslint: specifier: ^9.39.1 version: 9.39.2(jiti@1.21.7) @@ -141,13 +209,13 @@ importers: version: 1.5.2 postcss: specifier: '>=8.5.10' - version: 8.5.10 + version: 8.5.28 tailwindcss: specifier: ^3.4.17 - version: 3.4.19(tsx@4.21.0) + version: 3.4.19(tsx@4.21.0)(yaml@2.9.0) tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.19(tsx@4.21.0)) + version: 1.0.7(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0)) tsx: specifier: ^4.19.0 version: 4.21.0 @@ -156,91 +224,93 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.9 - version: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + version: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + + packages/design: {} packages/ui: dependencies: '@hookform/resolvers': specifier: ^5.2.2 - version: 5.2.2(react-hook-form@7.73.1(react@19.2.4)) + version: 5.2.2(react-hook-form@7.73.1(react@19.2.3)) '@mdx-js/mdx': specifier: ^3.1.1 version: 3.1.1 '@radix-ui/react-alert-dialog': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-aspect-ratio': specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-avatar': specifier: ^1.1.11 - version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-checkbox': specifier: ^1.3.3 - version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-collapsible': specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-context-menu': specifier: ^2.2.16 - version: 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-dialog': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-dropdown-menu': specifier: ^2.1.16 - version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-hover-card': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-label': specifier: ^2.1.8 - version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-menubar': specifier: ^1.1.16 - version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-navigation-menu': specifier: ^1.2.14 - version: 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-popover': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-radio-group': specifier: ^1.3.8 - version: 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-scroll-area': specifier: ^1.2.10 - version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-select': specifier: ^2.2.6 - version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-separator': specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-slider': specifier: ^1.3.6 - version: 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-slot': specifier: ^1.2.3 - version: 1.2.4(@types/react@19.2.13)(react@19.2.4) + version: 1.2.4(@types/react@19.2.13)(react@19.2.3) '@radix-ui/react-switch': specifier: ^1.2.6 - version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-toggle': specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-toggle-group': specifier: ^1.1.11 - version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-tooltip': specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-table': specifier: ^8.21.3 - version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@xyflow/react': specifier: ^12.10.0 - version: 12.10.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 12.10.0(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -249,101 +319,101 @@ importers: version: 2.1.1 cmdk: specifier: ^1.0.0 - version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) embla-carousel-react: specifier: ^8.6.0 - version: 8.6.0(react@19.2.4) + version: 8.6.0(react@19.2.3) html-to-image: specifier: ^1.11.13 version: 1.11.13 input-otp: specifier: ^1.4.2 - version: 1.4.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) lucide-react: specifier: ^0.468.0 - version: 0.468.0(react@19.2.4) + version: 0.468.0(react@19.2.3) next: specifier: '>=16.2.6' - version: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) next-themes: specifier: '>=0.4.0' - version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) qrcode: specifier: 1.5.4 version: 1.5.4 react: specifier: '>=19.0.0' - version: 19.2.4 + version: 19.2.3 react-day-picker: specifier: ^9.13.0 - version: 9.13.2(react@19.2.4) + version: 9.13.2(react@19.2.3) react-dom: specifier: '>=19.0.0' - version: 19.2.4(react@19.2.4) + version: 19.2.3(react@19.2.3) react-hook-form: specifier: ^7.73.1 - version: 7.73.1(react@19.2.4) + version: 7.73.1(react@19.2.3) react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.2.13)(react@19.2.4) + version: 10.1.0(@types/react@19.2.13)(react@19.2.3) react-resizable-panels: specifier: ^4.3.3 - version: 4.6.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 4.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-syntax-highlighter: specifier: ^16.1.1 - version: 16.1.1(react@19.2.4) + version: 16.1.1(react@19.2.3) remark-gfm: specifier: ^4.0.1 version: 4.0.1 sonner: specifier: ^1.7.4 - version: 1.7.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.7.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tailwind-merge: specifier: ^2.5.5 version: 2.6.1 vaul: specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) zod: specifier: ^4.3.6 version: 4.3.6 devDependencies: '@chromatic-com/storybook': specifier: ^5.0.1 - version: 5.0.1(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 5.0.1(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@github-ui/storybook-addon-performance-panel': specifier: ^1.1.4 - version: 1.1.4(@storybook/icons@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@storybook/react@10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 1.1.4(@storybook/icons@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@storybook/react@10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@playwright/experimental-ct-react': specifier: ^1.57.0 - version: 1.58.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + version: 1.58.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))(yaml@2.9.0) '@storybook/addon-a11y': specifier: ^10.2.17 - version: 10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@storybook/addon-designs': specifier: ^11.1.2 - version: 11.1.2(@storybook/addon-docs@10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 11.1.2(@storybook/addon-docs@10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@storybook/addon-docs': specifier: ^10.2.17 - version: 10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + version: 10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) '@storybook/addon-mcp': specifier: ^0.3.4 - version: 0.3.4(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + version: 0.3.4(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) '@storybook/addon-themes': specifier: ^10.2.17 - version: 10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@storybook/react-vite': specifier: ^10.2.17 - version: 10.2.17(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + version: 10.2.17(esbuild@0.27.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) '@storybook/test-runner': specifier: ^0.24.2 - version: 0.24.2(@types/node@22.19.10)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 0.24.2(@types/node@22.19.10)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@types/node': specifier: ^22 version: 22.19.10 @@ -364,13 +434,13 @@ importers: version: 4.1.9(vitest@4.1.9) '@vllnt/eslint-config': specifier: ^1.0.0 - version: 1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) + version: 1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.3))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) '@vllnt/typescript': specifier: ^1.0.0 version: 1.0.0 autoprefixer: specifier: ^10.4.21 - version: 10.4.24(postcss@8.5.10) + version: 10.4.24(postcss@8.5.28) axe-core: specifier: 4.11.1 version: 4.11.1 @@ -385,19 +455,19 @@ importers: version: 1.58.2 postcss: specifier: '>=8.5.10' - version: 8.5.10 + version: 8.5.28 storybook: specifier: ^10.2.17 - version: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tailwindcss: specifier: ^3.4.17 - version: 3.4.19(tsx@4.21.0) + version: 3.4.19(tsx@4.21.0)(yaml@2.9.0) tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.19(tsx@4.21.0)) + version: 1.0.7(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0)) tsup: specifier: ^8.5.0 - version: 8.5.1(@swc/core@1.15.18)(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0)(typescript@5.9.3) + version: 8.5.1(@swc/core@1.15.18)(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) tsx: specifier: ^4.21.0 version: 4.21.0 @@ -406,7 +476,77 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.9 - version: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + version: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + + packages/ui-core: + devDependencies: + '@vllnt/eslint-config': + specifier: ^1.0.0 + version: 1.0.0(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) + '@vllnt/typescript': + specifier: ^1.0.0 + version: 1.0.0 + eslint: + specifier: ^9.39.1 + version: 9.39.2(jiti@1.21.7) + tsup: + specifier: ^8.5.0 + version: 8.5.1(@swc/core@1.15.18)(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.1.9 + version: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + + packages/ui-native: + dependencies: + '@vllnt/ui-core': + specifier: workspace:* + version: link:../ui-core + devDependencies: + '@react-native/babel-preset': + specifier: 0.86.3 + version: 0.86.3(@babel/core@7.29.0) + '@testing-library/react-native': + specifier: ^13.3.3 + version: 13.3.3(jest@29.7.0(@types/node@22.19.10))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/react': + specifier: 19.2.13 + version: 19.2.13 + '@vllnt/eslint-config': + specifier: ^1.0.0 + version: 1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.3))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3) + '@vllnt/typescript': + specifier: ^1.0.0 + version: 1.0.0 + babel-jest: + specifier: ^29.7.0 + version: 29.7.0(@babel/core@7.29.0) + eslint: + specifier: ^9.39.1 + version: 9.39.2(jiti@1.21.7) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.10) + react: + specifier: 19.2.3 + version: 19.2.3 + react-native: + specifier: 0.86.3 + version: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + react-test-renderer: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + tsup: + specifier: ^8.5.0 + version: 8.5.1(@swc/core@1.15.18)(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 packages: @@ -424,10 +564,18 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.29.0': resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} @@ -436,32 +584,73 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.6': resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.28.5': resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.6': resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} engines: {node: '>=6.9.0'} @@ -472,32 +661,72 @@ packages: resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.28.6': resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.28.6': resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.6': resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} @@ -507,6 +736,23 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-decorators@7.29.7': + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-default-from@7.29.7': + resolution: {integrity: sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -528,6 +774,29 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-default-from@7.29.7': + resolution: {integrity: sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.29.7': + resolution: {integrity: sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-attributes@7.28.6': resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} engines: {node: '>=6.9.0'} @@ -550,6 +819,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -598,12 +873,138 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.29.7': + resolution: {integrity: sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-modules-commonjs@7.28.6': resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.29.7': + resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.29.7': + resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -616,12 +1017,42 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx@7.29.7': + resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.29.7': + resolution: {integrity: sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.8': + resolution: {integrity: sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.29.7': + resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@7.28.6': resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/preset-typescript@7.28.5': resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} engines: {node: '>=6.9.0'} @@ -636,18 +1067,34 @@ packages: resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -740,6 +1187,14 @@ packages: '@date-fns/tz@1.4.1': resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} + '@dependents/detective-less@5.0.3': + resolution: {integrity: sha512-v6oD9Ukp+N7V4n6p5I/+mM5fIohSfkrDSGlFm5w/pYmchvbk+sMIHsLxrFJ5Lnujewj1BzWL0K84d88lwZAMQA==} + engines: {node: '>=18'} + + '@discoveryjs/json-ext@1.1.0': + resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==} + engines: {node: '>=14.17.0'} + '@dotenvx/dotenvx@1.52.0': resolution: {integrity: sha512-CaQcc8JvtzQhUSm9877b6V4Tb7HCotkcyud9X2YwdqtQKwgljkMRwU96fVYKnzN3V0Hj74oP7Es+vZ0mS+Aa1w==} hasBin: true @@ -777,8 +1232,14 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -795,6 +1256,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -813,6 +1280,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -831,6 +1304,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -849,6 +1328,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -867,6 +1352,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -885,6 +1376,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -903,6 +1400,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -921,6 +1424,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -939,6 +1448,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -957,6 +1472,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -975,6 +1496,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -993,6 +1520,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -1011,6 +1544,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -1029,6 +1568,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -1047,6 +1592,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -1065,6 +1616,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} @@ -1083,6 +1640,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -1101,6 +1664,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} @@ -1119,6 +1688,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -1137,6 +1712,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} @@ -1155,6 +1736,12 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -1173,6 +1760,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -1191,6 +1784,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -1209,6 +1808,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -1227,6 +1832,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1265,6 +1876,162 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@expo/cli@57.0.21': + resolution: {integrity: sha512-CkcgAOsp1KkNRENXWfyr/esIwgFtiHosKECWZ0QQl2nFOUEQlyjWXAZ6uTCMnbHLonVnPMZpXTkHZNw3gtanFw==} + hasBin: true + peerDependencies: + expo: '*' + expo-router: '*' + react-native: '*' + peerDependenciesMeta: + expo-router: + optional: true + react-native: + optional: true + + '@expo/code-signing-certificates@0.0.6': + resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} + + '@expo/config-plugins@57.0.9': + resolution: {integrity: sha512-hHgfL1avkCdEvDSw7IwlKwRYYNgcxzbNNMIk6W6lTkJpY0MajinAfeJUS0J+wPCsjUfGbVqOJM+XhPaO5ulUxg==} + + '@expo/config-types@57.0.2': + resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} + + '@expo/config@57.0.9': + resolution: {integrity: sha512-dmzlKraIFxa7wLwV6K7WzI8jp6QZpW6Mc5mGjLimJUFjzh4uQdYaT3m3plEutM5yxBoBEwqzks7l+I/ljCbxAQ==} + + '@expo/devcert@1.2.1': + resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} + + '@expo/devtools@57.0.1': + resolution: {integrity: sha512-GyUf+wFNkbttaX0jR7MZa9bm77U0IrLg6d2AjpxdyoXw/w4abHoXG0oFufwLMgP9zLTd5+Ct4X/ffNUTnlzZgg==} + peerDependencies: + react: '*' + react-native: '*' + peerDependenciesMeta: + react: + optional: true + react-native: + optional: true + + '@expo/dom-webview@57.0.1': + resolution: {integrity: sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + '@expo/env@2.4.3': + resolution: {integrity: sha512-M1NXeZCA1mkMkYOyIe7PlyRX0/jqFtMoJgyblnlq/vpCRfmueFT7RnGSQG8uEFDF5WHOFGijAQ3fogPh3/n5Ng==} + engines: {node: '>=20.12.0'} + + '@expo/expo-modules-macros-plugin@0.6.1': + resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==} + + '@expo/fingerprint@0.20.12': + resolution: {integrity: sha512-FIR5fkZYeFaLSowmjgyB6RPKl8AXeE8HuCBHHvyxK8UhOjpPfCAmzT4E7v2yub4qqDafKnfcOFCYxvpUEu+01w==} + hasBin: true + + '@expo/image-utils@0.11.5': + resolution: {integrity: sha512-KPQBTpmpAfy/Vu9y4wPW808/qtZxjYmyJg8cm2QCPAupp+qEWA3b5zmk0ulOwQ9OgeHxuCPgUqWgkwHFo7UsrQ==} + + '@expo/inline-modules@0.1.7': + resolution: {integrity: sha512-Bz/khd1gIJqDkje7t5ejD5e9jFbm4xEJzWSwRKadRo6gruepbw1xJ3Eb+e58OS8fY1ZNQYjzZbspnuph67sN0g==} + + '@expo/json-file@11.0.1': + resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==} + + '@expo/local-build-cache-provider@57.0.8': + resolution: {integrity: sha512-SEdE0pAQrr90bRh3MNR0ZuwoIBw390cYdFgbn7Vk0Mtm9EHaBfP7kYj+2hXnXZJgOEm3/JMtfoD3rV7rWA9FGg==} + + '@expo/log-box@57.0.4': + resolution: {integrity: sha512-IxwS9s1L2muj8mj8AQSuiy7u8OFJdc02NRFo2me/Tj6DiaeG5SREqmpBE4rQpR2cadqSg5jl8Qab8Cjie616dg==} + peerDependencies: + '@expo/dom-webview': ^57.0.1 + expo: '*' + react: '*' + react-native: '*' + + '@expo/metro-config@57.0.12': + resolution: {integrity: sha512-S62Lrq35HZqBFD55423pmWb8PjaiR/W02zQC1uECBmw1vTN8WZaFz4TJ0i21EeJzwfebMb9MLxL8JZ102Z6VbA==} + peerDependencies: + expo: '*' + peerDependenciesMeta: + expo: + optional: true + + '@expo/metro-file-map@57.0.2': + resolution: {integrity: sha512-tb50nSIWwKpRufSkGuivOK0FbUxv1Uwqptb0SzFTk0bkmYmcy3gIwnCbOHTfmrQDVndRhPEiu2SYgh9Z0PCvGg==} + + '@expo/metro@56.0.2': + resolution: {integrity: sha512-Ld5AeYMCCDa8bLeWhfuLbZFFjlV3f6ORqyPz2glGh6RltIngMuLf9BTC2yvHFjkKuGxL5SynijmA8xmNNWn5iA==} + + '@expo/osascript@2.7.1': + resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==} + engines: {node: '>=12'} + + '@expo/package-manager@1.13.1': + resolution: {integrity: sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==} + + '@expo/plist@0.8.1': + resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} + + '@expo/prebuild-config@57.0.15': + resolution: {integrity: sha512-xTbWHroj0PDmlbqvmU+zF9ZZxveJkiuyiPoeRJYRGruFHebRAWnoTdw5S7d/UCzDBI8ropGGu9g2eb2nMxtvAw==} + + '@expo/require-utils@57.0.5': + resolution: {integrity: sha512-kTAXj9lDFEIPMsbAOGCGbjBbMF0oi7CqkYM79KOX0DDD9wSwXmlKL1z2h8OwsrBf7mbOo2DjlRvZu4BEjrIxGw==} + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@expo/router-server@57.0.9': + resolution: {integrity: sha512-/PxRQozFesIyCJZOAtrQE8XcmcojNiL5ctPMQnbE4ojC2EJPu0zc7c0Y4PuXsoRxrZxm8Usw76/lCVrXcfTZ2w==} + peerDependencies: + '@expo/metro-runtime': ^57.0.15 + expo: '*' + expo-constants: ^57.0.17 + expo-font: ^57.0.3 + expo-router: '*' + expo-server: ^57.0.3 + react: '*' + react-dom: '*' + react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 + peerDependenciesMeta: + '@expo/metro-runtime': + optional: true + expo-router: + optional: true + react-dom: + optional: true + react-server-dom-webpack: + optional: true + + '@expo/schema-utils@57.0.2': + resolution: {integrity: sha512-fMu/jyN0l1Wzv7XkeWR4IYCx1M8ryui3FdBNGrWwbRgJ7EhxXxK8E2jxP2W3pbgUwUY0V3hG8+GyfCZwny+Lxw==} + + '@expo/sdk-runtime-versions@1.0.0': + resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} + + '@expo/spawn-async@1.8.0': + resolution: {integrity: sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==} + engines: {node: '>=12'} + + '@expo/sudo-prompt@9.3.2': + resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} + + '@expo/ws-tunnel@2.0.0': + resolution: {integrity: sha512-j+JfTRdCk820J9dU0sA2SqshQIKFOMo7ED84w9MJFcebfbNQgsLztEY/SABDkGnjatrW4xGqnUhVRxSBVyCkXw==} + peerDependencies: + ws: 8.21.3 + + '@expo/xcpretty@4.4.4': + resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} + hasBin: true + '@figspec/components@2.1.0': resolution: {integrity: sha512-PFKBX2oFz+vhThKTNsu7Mh4ZT3X7YCiM694UkAMT36j/p0tdmXs9Je0Sf88stTEcMgwYvNv9TZtvniYmgaE+bw==} @@ -1522,6 +2289,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/ttlcache@1.4.1': + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -1530,10 +2301,23 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/console@30.3.0': resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + '@jest/core@30.3.0': resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1543,6 +2327,10 @@ packages: node-notifier: optional: true + '@jest/create-cache-key-function@29.7.0': + resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/create-cache-key-function@30.3.0': resolution: {integrity: sha512-hTupmOWylzeyqbMNeSNi7ZDprpjrcroAOOG+qCEW66st3+Z5RnYHVYkUt+zjIcLmrTUi2lPY79hJz8mB3L2oXQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1551,18 +2339,34 @@ packages: resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/environment@30.3.0': resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/expect-utils@30.3.0': resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/expect@30.3.0': resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/fake-timers@30.3.0': resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1571,6 +2375,10 @@ packages: resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/globals@30.3.0': resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1579,6 +2387,15 @@ packages: resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + '@jest/reporters@30.3.0': resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1588,6 +2405,10 @@ packages: node-notifier: optional: true + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@30.0.5': resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1596,22 +2417,42 @@ packages: resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/source-map@30.0.1': resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/test-result@30.3.0': resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/test-sequencer@30.3.0': resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/transform@30.3.0': resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/types@30.3.0': resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1635,6 +2476,9 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -1693,6 +2537,12 @@ packages: resolution: {integrity: sha512-7G0Uf0yK3f2bjElBLGHIQzgRgMESczOMyYVasq1XK8P5HaXtlW4eQhz9MBL+TQILZLaruq+ClGId+hH0w4jvWw==} engines: {node: '>=18'} + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} @@ -2550,6 +3400,81 @@ packages: peerDependencies: react: '>=16.8' + '@react-native/assets-registry@0.86.3': + resolution: {integrity: sha512-TDhgCZA4wjJg84d5A9swiOQYPIWSKEEVdg9IwMFZDupQzW/F3QoLUrfAJOcalgqTDA9/buTB8awhE3Whwg6u9Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-plugin-codegen@0.86.3': + resolution: {integrity: sha512-O6Xza4JBGPIU8J7YbKTyBoYL4thpy8jMW/oaLDWdAyOwYHKIjK47pAL5HUEbOe2bWz2PEKjbYRF2ApkJv1ottQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-preset@0.86.3': + resolution: {integrity: sha512-/eqs/Hy9RZRcjdcs4wj3Cqmxvtb3NM5g+Uuh1RIvsjynMO8PRsrVWWLWgBcZL/jYUo+ogXd2NFB0W8L5Bg89xw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/codegen@0.86.3': + resolution: {integrity: sha512-Ux4jHi0fh+bdtVEcL0gaPLbY56V+SvFUDl/8sRAE1jdb4k+o7fT/4Nc29yz4X+qfjstkSqObQTMBGhdzxH9JvA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/community-cli-plugin@0.86.3': + resolution: {integrity: sha512-qSDL9LQc5mZSZPNczT95WU9YQuPzxBklgON9vLhhqfI0yWIwKInqFx88dQ/uiEXBtf0yossthaQIqA4Ml6bF6g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@react-native-community/cli': '*' + '@react-native/metro-config': 0.86.3 + peerDependenciesMeta: + '@react-native-community/cli': + optional: true + '@react-native/metro-config': + optional: true + + '@react-native/debugger-frontend@0.86.3': + resolution: {integrity: sha512-TQmeofQ0PcuylhhlleOeuzHYZfbrgm3gayXzowqUEzgRisTm1D40/J3ggqs7XkQi5HP5ZA3n8dHmKL9vIzPcsw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/debugger-shell@0.86.3': + resolution: {integrity: sha512-O4ds+J7xZfxkbih9T+cAGegBdvKSPKYJm/lDgC9CpEjFMkmzWTpVLU3Qsv9sqZuo58z+sGhIcJfPsCFFWHpqbQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/dev-middleware@0.86.3': + resolution: {integrity: sha512-LiEPTqTg/63bYUnrPyHLfjTDCNhA/+CUqI1+DsA9tYyewtSbULd5awsva6SgE10I+2iMhgKXS3ymkhU/kSCrGA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/gradle-plugin@0.86.3': + resolution: {integrity: sha512-lxmx0GqLEWRIpZfpYFXlYVIs3ENQwaW6Vmp6oi29l2GoQJ1wZfFZRdMimDWlGEk8LKfHar3QH3iaPMkTcK9lEQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/jest-preset@0.86.3': + resolution: {integrity: sha512-oMmBwXUpTgJvGlcXQQPuiQLykoM8jsWYp6RwdJ2NWyIWzJcGp1rmSr3XV7istoIcUt0wdOVLtQs0A0nDm9ND+A==} + engines: {node: '>= 20.19.4'} + peerDependencies: + react: ^19.2.3 + + '@react-native/js-polyfills@0.86.3': + resolution: {integrity: sha512-eYIJ0es967+tePBFQDnl/gidVFxLns3fnbiK6rxscQrGodvuUO6hwxpQnfNynJ8MjbbndImXihXjcnJdc7SzJg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/normalize-colors@0.74.89': + resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} + + '@react-native/normalize-colors@0.86.3': + resolution: {integrity: sha512-Cv3CDkprb67GrzuaS9BGbBJC/6G4lIw3nyKOHRKTqTTum4bn37y5+R0Z04L8mcbQN85eEohNrRwb7IOM4j6uvg==} + + '@react-native/virtualized-lists@0.86.3': + resolution: {integrity: sha512-1j44NEyNn05Ut40vHAmoSWbsIcybFkMAOBTwQt1PrESyfSS+qBoyU1LGIogNva0VIa0rQyEC5PzbA7R4/7Nhyw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@types/react': ^19.2.0 + react: '*' + react-native: 0.86.3 + peerDependenciesMeta: + '@types/react': + optional: true + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -2567,53 +3492,108 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.63.1': + resolution: {integrity: sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.59.0': resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.63.1': + resolution: {integrity: sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.59.0': resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.63.1': + resolution: {integrity: sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.59.0': resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.63.1': + resolution: {integrity: sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.59.0': resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.63.1': + resolution: {integrity: sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.59.0': resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.63.1': + resolution: {integrity: sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + resolution: {integrity: sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.63.1': + resolution: {integrity: sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.63.1': + resolution: {integrity: sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-arm64-musl@4.63.1': + resolution: {integrity: sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.63.1': + resolution: {integrity: sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==} cpu: [loong64] os: [linux] @@ -2622,71 +3602,141 @@ packages: cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loong64-musl@4.63.1': + resolution: {integrity: sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.63.1': + resolution: {integrity: sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-musl@4.63.1': + resolution: {integrity: sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.63.1': + resolution: {integrity: sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.63.1': + resolution: {integrity: sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.63.1': + resolution: {integrity: sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.63.1': + resolution: {integrity: sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.63.1': + resolution: {integrity: sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==} + cpu: [x64] + os: [linux] + '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} cpu: [x64] os: [openbsd] + '@rollup/rollup-openbsd-x64@4.63.1': + resolution: {integrity: sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==} + cpu: [x64] + os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.59.0': resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.63.1': + resolution: {integrity: sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.59.0': resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.63.1': + resolution: {integrity: sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.59.0': resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.63.1': + resolution: {integrity: sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.59.0': resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.63.1': + resolution: {integrity: sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.59.0': resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.63.1': + resolution: {integrity: sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==} + cpu: [x64] + os: [win32] + '@schummar/icu-type-parser@1.21.5': resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} @@ -2702,6 +3752,9 @@ packages: '@sideway/pinpoint@2.0.0': resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + '@sinclair/typebox@0.27.12': + resolution: {integrity: sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==} + '@sinclair/typebox@0.34.48': resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} @@ -2712,6 +3765,9 @@ packages: '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@sinonjs/fake-timers@15.1.1': resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==} @@ -2940,6 +3996,18 @@ packages: resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/react-native@13.3.3': + resolution: {integrity: sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==} + engines: {node: '>=18'} + peerDependencies: + jest: '>=29.0.0' + react: '>=18.2.0' + react-native: '>=0.71' + react-test-renderer: '>=18.2.0' + peerDependenciesMeta: + jest: + optional: true + '@testing-library/react@16.3.2': resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} engines: {node: '>=18'} @@ -2981,6 +4049,22 @@ packages: '@tmcp/auth': optional: true + '@ts-graphviz/adapter@2.0.6': + resolution: {integrity: sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==} + engines: {node: '>=18'} + + '@ts-graphviz/ast@2.0.7': + resolution: {integrity: sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw==} + engines: {node: '>=18'} + + '@ts-graphviz/common@2.1.5': + resolution: {integrity: sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==} + engines: {node: '>=18'} + + '@ts-graphviz/core@2.0.7': + resolution: {integrity: sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg==} + engines: {node: '>=18'} + '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} @@ -3038,6 +4122,12 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -3050,6 +4140,9 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -3142,6 +4235,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.69.0': + resolution: {integrity: sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@8.49.0': resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3172,6 +4271,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.69.0': + resolution: {integrity: sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.55.0': resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3198,6 +4303,10 @@ packages: resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.69.0': + resolution: {integrity: sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.49.0': resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3216,6 +4325,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.69.0': + resolution: {integrity: sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.49.0': resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3249,6 +4364,10 @@ packages: resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.69.0': + resolution: {integrity: sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} deprecated: Potential CWE-502 - Update to 1.3.1 or higher @@ -3469,14 +4588,37 @@ packages: typescript: optional: true - '@vllnt/next-llms@0.1.0-canary.78c9be3': - resolution: {integrity: sha512-KVx3d1Zk3P+Cyn75IBFjIuybv5CKPrzeAT20riKNMBPmh09wjx6NWr0X8qZY3WIl5+/ADasTX8W650lqfCRc2Q==} + '@vllnt/next-llms@0.1.0-canary.bfc9152': + resolution: {integrity: sha512-+TnNJ/p80P7kvjY9Q5mjULs9GPEzUDMG3vbCidJycu/c1AWRVpx+HTvvmbTDvJF6jfUmGiHps+6BcPZfHG6c1A==} engines: {node: '>=18'} '@vllnt/typescript@1.0.0': resolution: {integrity: sha512-0hr7yBLLWb6Z5QN3a4IF9TQyU8v8/NoygCAuL5LG64vrHaLdXccIuXzkkOJxBmSVj5dmoxVbJ8IvuCTHC/3IWA==} engines: {node: '>=22'} + '@vue/compiler-core@3.5.42': + resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==} + + '@vue/compiler-dom@3.5.42': + resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==} + + '@vue/compiler-sfc@3.5.42': + resolution: {integrity: sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==} + + '@vue/compiler-ssr@3.5.42': + resolution: {integrity: sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==} + + '@vue/shared@3.5.42': + resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==} + + '@xmldom/xmldom@0.8.15': + resolution: {integrity: sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==} + engines: {node: '>=10.0.0'} + + '@xmldom/xmldom@0.9.12': + resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} + engines: {node: '>=14.6'} + '@xyflow/react@12.10.0': resolution: {integrity: sha512-eOtz3whDMWrB4KWVatIBrKuxECHqip6PfA8fTpaS2RUGVpiEAe+nqDKsLqkViVWxDGreq0lWX71Xth/SPAzXiw==} peerDependencies: @@ -3486,6 +4628,14 @@ packages: '@xyflow/system@0.0.74': resolution: {integrity: sha512-7v7B/PkiVrkdZzSbL+inGAo6tkR/WQHHG0/jhSvLQToCsfa8YubOGmBYd1s08tpKpihdHDZFwzQZeR69QSBb4Q==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3513,6 +4663,11 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-cli-detector@0.1.7: + resolution: {integrity: sha512-d8OWDVdZMgjhLUT9ZPgSv/BdFFF9pVuscC0JdUSz3bjwE15gcp6u/o0/JooM2yyAWC49KThFhXlgTXRa9B7yng==} + engines: {node: '>=18.18'} + hasBin: true + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -3531,6 +4686,9 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + anser@1.4.10: + resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} + anser@2.3.5: resolution: {integrity: sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==} @@ -3542,6 +4700,10 @@ packages: resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} engines: {node: '>=18'} + ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3573,6 +4735,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-module-path@2.2.0: + resolution: {integrity: sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==} + append-transform@2.0.0: resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} engines: {node: '>=8'} @@ -3628,10 +4793,17 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-module-types@6.0.2: + resolution: {integrity: sha512-6KuK/7nZ/2Qh7sGuVEiwxjCxzTY2Pdb5mTo5z1e6/J8BA0tvjR7G8vQJKrQMTqwmnA3UPEyKIFX4YUS1DO1Hvw==} + engines: {node: '>=18'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -3675,25 +4847,90 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + babel-jest@30.3.0: resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + babel-plugin-istanbul@7.0.1: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} engines: {node: '>=12'} + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + babel-plugin-jest-hoist@30.3.0: resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + + babel-plugin-react-native-web@0.21.2: + resolution: {integrity: sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==} + + babel-plugin-syntax-hermes-parser@0.36.0: + resolution: {integrity: sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q==} + + babel-plugin-syntax-hermes-parser@0.36.1: + resolution: {integrity: sha512-ycduwJbvdvIMmVvlAZqGggS+pm5Eu4Bk9pcV9Sm2Z4PJNRVsKkv0g7vHj+LeuC1gHTeF67sJXFOq61IlqCa2hA==} + + babel-plugin-transform-flow-enums@0.0.2: + resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} + babel-preset-current-node-syntax@1.2.0: resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 + babel-preset-expo@57.0.10: + resolution: {integrity: sha512-08bt6bqMZFoQuWO9gkt0EXotbqKHimELFGa1LlKJvY89iKsi4S0FJSFBFJ4pn9NZvofet48HIjYDQ0SZJ7bNrg==} + peerDependencies: + '@babel/runtime': ^7.20.0 + expo: '*' + expo-widgets: ^57.0.16 + react-refresh: '>=0.14.0 <1.0.0' + peerDependenciesMeta: + '@babel/runtime': + optional: true + expo: + optional: true + expo-widgets: + optional: true + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + babel-preset-jest@30.3.0: resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3717,14 +4954,32 @@ packages: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + bplist-creator@0.1.0: + resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} + + bplist-parser@0.3.1: + resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} + engines: {node: '>= 5.10.0'} + + bplist-parser@0.3.2: + resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==} + engines: {node: '>= 5.10.0'} + brace-expansion@1.1.13: resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} @@ -3747,6 +5002,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -3873,10 +5131,28 @@ packages: '@chromatic-com/playwright': optional: true + chrome-launcher@0.15.2: + resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==} + engines: {node: '>=12.13.0'} + hasBin: true + + chromium-edge-launcher@0.3.0: + resolution: {integrity: sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==} + + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -3897,6 +5173,14 @@ packages: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} + cli-cursor@2.1.0: + resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} + engines: {node: '>=4'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -3919,6 +5203,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -3984,15 +5272,31 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -4055,13 +5359,24 @@ packages: typescript: optional: true + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-in-js-utils@3.1.0: + resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -4152,6 +5467,14 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -4191,6 +5514,10 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -4214,6 +5541,9 @@ packages: resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} engines: {node: '>=8'} + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -4234,10 +5564,19 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dependency-tree@11.5.0: + resolution: {integrity: sha512-K9zBwKDZrot3RkxizugpVSdImxULAg4Ycp3+ydy2r561k96oiiw6nfsOR15fwNDQ5BF2UXe+2JFM/H5Xz4MGQg==} + engines: {node: '>=18'} + hasBin: true + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -4249,12 +5588,59 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + detective-amd@6.1.0: + resolution: {integrity: sha512-fmI6LGMvotqd49QaA3ZYw+q0aGp2yXmMjzIuY6fH9j9YFIXY/73yDhMwhX9cPbhWd+AH06NH1Di/LKOuCH0Ubg==} + engines: {node: '>=18'} + hasBin: true + + detective-cjs@6.1.1: + resolution: {integrity: sha512-pSh7mkCKEtLlmANqLu3KDFS3NV8Hx41jy/JF1/gAWOgU+Uo5QTkeI1tWNP4dWGo4L0E9j18Ez9EPsTleautKqA==} + engines: {node: '>=18'} + + detective-es6@5.0.2: + resolution: {integrity: sha512-+qHHGYhjupiVs4rnIpI9nZ5B130A4AmE35ZX1w33hb46vcZ7T3jfDbvmPw0FhWtMHn5BS5HHu7ZtnZ53bMcXZA==} + engines: {node: '>=18'} + + detective-postcss@8.0.4: + resolution: {integrity: sha512-DZ7M/hWPZyr17ZUdoQ+TVXaPj70mYr4XXrAE+GeJbca44haCvZgb191L/jLJmFYewhxRJuBd4lUtNSu986TXag==} + engines: {node: '>=18'} + peerDependencies: + postcss: '>=8.5.10' + + detective-sass@6.0.2: + resolution: {integrity: sha512-i3xpXHDKS0qI2aFW4asQ7fqlPK00ndOVZELvQapFJCaF0VxYmsNWtd0AmvXbTLMk7bfO5VdIeorhY9KfmHVoVA==} + engines: {node: '>=18'} + + detective-scss@5.0.2: + resolution: {integrity: sha512-9JOEMZ8pDh3ShXmftq7hoQqqJsClaGgxo1hghfCeFlmKf5TC/Twtwb0PAaK8dXwpg9Z0uCmEYSrCxO+kel2eEg==} + engines: {node: '>=18'} + + detective-stylus@5.0.1: + resolution: {integrity: sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA==} + engines: {node: '>=18'} + + detective-typescript@14.1.2: + resolution: {integrity: sha512-bIeEn0eVi/JRsE1YizBR2ilnMlWRAIBJJ6kXCKNFxEEWhUcEY3R6I3KYIAy48ieURbD1hcb3Ebvl8AqeoPMSzg==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 || ^6.0.2 + + detective-vue2@2.3.0: + resolution: {integrity: sha512-3gwbZPqVTm9sL9XdZsgEJ7x4x99O853VVZHapQAiEkGuMJMpFPjHDrecSgfqnS5JW3FJfYXesLZGvUOibjn49g==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 || ^6.0.2 + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@8.0.3: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} @@ -4268,6 +5654,9 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dnssd-advertise@1.1.6: + resolution: {integrity: sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -4355,10 +5744,18 @@ packages: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + entities@1.1.2: resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} @@ -4369,6 +5766,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -4380,6 +5781,9 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + es-abstract@1.24.1: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} @@ -4450,6 +5854,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -4476,6 +5885,11 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-config-prettier@10.1.8: resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true @@ -4606,6 +6020,7 @@ packages: eslint@9.39.2: resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -4676,6 +6091,10 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventsource-parser@3.0.6: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} @@ -4712,10 +6131,107 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + expect@30.3.0: resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + expo-asset@57.0.16: + resolution: {integrity: sha512-IBRfQdW3iFT+GOBERMZLZM1MUNyrjMgMskuD0elVZ1ae44858UFlTJkHO3f8vi+u5zuv7O7KofsiN8NMG/uWzw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-constants@57.0.17: + resolution: {integrity: sha512-cPWYBKN1SEbg2lXg2f8VkePJqGZrJPLvVdQDCTfbFu9sQHO1M31Y1zILReUuGnmt/VKeUz40ze579vR00xGu/A==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-doctor@1.20.4: + resolution: {integrity: sha512-Jm1lIqVdO8br9wiqTg5QhJ1u4zLWAhoue4bXsEuzW543vC3XY+U0cpEOmiLkvJhTTgZgIK+24l4i8VzyZ2VLMg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + expo-file-system@57.0.6: + resolution: {integrity: sha512-pm8PMYEW6BnVOCBJ7df9FcDmQtE1tqImuYphlfYe1ipQRLYtdCayRczJcbKIsnB3mqEyp4gC2gWmMbsAsAOjVg==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-font@57.0.3: + resolution: {integrity: sha512-kiVUnc2A8vAvO2FfDJTsQa5BwmY+PAkof/1wRb5MOkcX1jtiaSTwz9gCUAyscMBFLundZDmZrLy1P7LZVC+NvA==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-keep-awake@57.0.1: + resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} + peerDependencies: + expo: '*' + react: '*' + + expo-modules-autolinking@57.0.12: + resolution: {integrity: sha512-Q8KAlq37nLKsQ+HsS9NpQVpd5jCgqtu694TDUNHBUBpV9ViD82mRBh8Uug/h68RG9xnLS+kuL4nYaCuFRghHjg==} + hasBin: true + + expo-modules-core@57.0.15: + resolution: {integrity: sha512-HBxPXsx3eVLLRjAY57/8ghKDWIxjaEbluKKYFIq1OHPJC85ZfHsawwc0SOU4Z/qu+mdLz7Pwt5IkkWimeNSH5A==} + peerDependencies: + react: '*' + react-native: '*' + react-native-worklets: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + peerDependenciesMeta: + react-native-worklets: + optional: true + + expo-modules-jsi@57.0.7: + resolution: {integrity: sha512-/GOrTFPnCfg7+m6M1yOSEKNeNdHBCm0ju6aJ4XHlSmtE02nb7mWh1Cs6QsK2eB5Rz7hjbb356Nd7UimsFvN7Vg==} + peerDependencies: + react-native: '*' + + expo-server@57.0.3: + resolution: {integrity: sha512-aK+LdKzauHSGmsOStZtyxdzv0zWssCkxTw3m4QuOhfDSJsZaMRTd9O41d8ixU/QfELTbaJ0oRNcF7JFV/7O9YQ==} + engines: {node: '>=20.16.0'} + + expo-status-bar@57.0.1: + resolution: {integrity: sha512-Xwaq1gAoVRWx5dPG5VhT5RSbnI9OilhZnO5qoPBnUaBAa5VzRzfdS8q0/bsPt0jR2DKLtGuP0bQ6efMJ4RIMDg==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo@57.0.19: + resolution: {integrity: sha512-oIoAIisim1DwS2Zjp0rcEY9cfcq1wU6KpkBfwwFhJWI5YHV6i/aIzTUMX+uf0Q0CD6AvMls7IZVh34o27NjaJA==} + hasBin: true + peerDependencies: + '@expo/dom-webview': '*' + '@expo/metro-runtime': '*' + react: '*' + react-dom: '*' + react-native: '*' + react-native-web: '*' + react-native-webview: '*' + peerDependenciesMeta: + '@expo/dom-webview': + optional: true + '@expo/metro-runtime': + optional: true + react-dom: + optional: true + react-native-web: + optional: true + react-native-webview: + optional: true + + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + express-rate-limit@8.2.2: resolution: {integrity: sha512-Ybv7bqtOgA914MLwaHWVFXMpMYeR1MQu/D+z2MaLYteqBsTIp9sY3AU7mGNLMJv8eLg8uQMpE20I+L2Lv49nSg==} engines: {node: '>= 16'} @@ -4765,9 +6281,20 @@ packages: fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fb-dotslash@0.5.8: + resolution: {integrity: sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==} + engines: {node: '>=20'} + hasBin: true + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -4781,6 +6308,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fetch-nodeshim@0.4.10: + resolution: {integrity: sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -4793,11 +6323,20 @@ packages: resolution: {integrity: sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==} engines: {node: '>= 10.4.0'} + filing-cabinet@5.5.1: + resolution: {integrity: sha512-PzLBTChlVPn6LnNxF0KWs+XqPziVh3Sfmz/3TXOymHxu6a9yhrDcQn7YwgpcRM6mqhR2WHVGPR8RU4fmcF1IVA==} + engines: {node: '>=18'} + hasBin: true + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@2.1.1: + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} @@ -4839,6 +6378,9 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + flow-enums-runtime@0.0.6: + resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==} + follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -4848,6 +6390,9 @@ packages: debug: optional: true + fontfaceobserver@2.3.0: + resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -4879,6 +6424,10 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + fresh@2.0.0: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} @@ -4928,6 +6477,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-amd-module-type@6.0.2: + resolution: {integrity: sha512-7zShVYAYtMnj9S65CfN+hvpBCByfuB1OY8xID01nZEzXTZbx4YyysAfi+nMl95JSR6odt4q8TCj2W63KAoyVLQ==} + engines: {node: '>=18'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -4948,6 +6501,9 @@ packages: resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} engines: {node: '>=14.16'} + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} @@ -4971,6 +6527,10 @@ packages: get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + getenv@2.0.0: + resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} + engines: {node: '>=6'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5090,12 +6650,33 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hermes-compiler@250829098.0.17: + resolution: {integrity: sha512-qG1PXzTEtriF6oQLZF3vyHhSMxOdW5h2TqqLri0rdpstPustd2fSvRZQMVAPdlhgFwBfYnj3OUZtiO6LjYsEFw==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + hermes-estree@0.35.0: + resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==} + + hermes-estree@0.36.0: + resolution: {integrity: sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w==} + + hermes-estree@0.36.1: + resolution: {integrity: sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw==} + hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hermes-parser@0.35.0: + resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} + + hermes-parser@0.36.0: + resolution: {integrity: sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==} + + hermes-parser@0.36.1: + resolution: {integrity: sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -5110,6 +6691,10 @@ packages: resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==} engines: {node: '>=16.9.0'} + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -5146,6 +6731,9 @@ packages: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -5202,6 +6790,9 @@ packages: inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + inline-style-prefixer@7.0.1: + resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} + input-otp@1.4.2: resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} peerDependencies: @@ -5219,6 +6810,9 @@ packages: intl-messageformat@11.2.8: resolution: {integrity: sha512-l323RCl3qJDVQ8U9j74ut/hVMdg3VPsOHpVMDvFfz9qiq4dPO5ooVYFNVUzzrpgG39a+RLzcXyJb8VFgIU+tUA==} + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + ip-address@10.1.1: resolution: {integrity: sha512-1FMu8/N15Ck1BL551Jf42NYIoin2unWjLQ2Fze/DXryJRl5twqtwNHlO39qERGbIOcKYWHdgRryhOC+NG4eaLw==} engines: {node: '>= 12'} @@ -5279,6 +6873,11 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5330,6 +6929,10 @@ packages: engines: {node: '>=14.16'} hasBin: true + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -5353,6 +6956,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + is-obj@3.0.0: resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} engines: {node: '>=12'} @@ -5371,6 +6978,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + is-regexp@3.1.0: resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} engines: {node: '>=12'} @@ -5406,6 +7017,10 @@ packages: is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -5414,6 +7029,10 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-url-superb@4.0.0: + resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} + engines: {node: '>=10'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -5434,6 +7053,10 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + is-wsl@3.1.1: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} @@ -5460,6 +7083,10 @@ packages: resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} engines: {node: '>=8'} + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} @@ -5491,14 +7118,32 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-changed-files@30.3.0: resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-circus@30.3.0: resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jest-cli@30.3.0: resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5509,6 +7154,18 @@ packages: node-notifier: optional: true + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + jest-config@30.3.0: resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5524,22 +7181,46 @@ packages: ts-node: optional: true + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-diff@30.3.0: resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-docblock@30.2.0: resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-each@30.3.0: resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-environment-node@30.3.0: resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-haste-map@30.3.0: resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5548,18 +7229,34 @@ packages: resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==} engines: {node: '>=10.12.0'} + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-leak-detector@30.3.0: resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-matcher-utils@30.3.0: resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-message-util@30.3.0: resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-mock@30.3.0: resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5577,22 +7274,42 @@ packages: resolution: {integrity: sha512-80Y6snDyb0p8GG83pDxGI/kQzwVTkCxc7ep5FPe/F6JYdvRDhwr6RzRmPSP7SEwuLhxo80lBS/NqOdUIbHIfhw==} deprecated: ⚠️ The 'jest-process-manager' package is deprecated. Please migrate to Playwright's built-in test runner (@playwright/test) which now includes full Jest-style features and parallel testing. See https://playwright.dev/docs/intro for details. + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-regex-util@30.0.1: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-resolve-dependencies@30.3.0: resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-resolve@30.3.0: resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-runner@30.3.0: resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-runtime@30.3.0: resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5600,14 +7317,26 @@ packages: jest-serializer-html@7.1.0: resolution: {integrity: sha512-xYL2qC7kmoYHJo8MYqJkzrl/Fdlx+fat4U1AqYg+kafqwcKPiMkOcjWHPKhueuNEgr+uemhGc+jqXYiwCyRyLA==} + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-snapshot@30.3.0: resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-util@30.3.0: resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-validate@30.3.0: resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5618,14 +7347,32 @@ packages: peerDependencies: jest: ^30.0.0 + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-watcher@30.3.0: resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-worker@30.3.0: resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jest@30.3.0: resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5636,6 +7383,9 @@ packages: node-notifier: optional: true + jimp-compact@0.16.1: + resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} + jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -5664,6 +7414,9 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsc-safe-url@0.2.4: + resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} + jsdom@26.1.0: resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} engines: {node: '>=18'} @@ -5729,6 +7482,10 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + lan-network@0.2.1: + resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==} + hasBin: true + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -5744,6 +7501,79 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lighthouse-logger@1.4.2: + resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -5763,15 +7593,29 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.flattendeep@4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + log-symbols@2.2.0: + resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} + engines: {node: '>=4'} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} @@ -5812,6 +7656,16 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + madge@8.0.0: + resolution: {integrity: sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: ^5.4.4 + peerDependenciesMeta: + typescript: + optional: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -5836,6 +7690,9 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marky@1.3.0: + resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -5892,6 +7749,12 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -5903,6 +7766,122 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + metro-babel-transformer@0.84.5: + resolution: {integrity: sha512-2WbHILKMiJUzfdjmGOQOqU1bWi9//gqiclc/tkk/AIsrrVw3efhZ1uhkOwMTxUEPOzqoo091H0olLmVZH5FHGQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-babel-transformer@0.84.6: + resolution: {integrity: sha512-B1ozl6KxFHbQjXZ2U8WiTPWXylqiD3V4EXyV5r0fqETOrqIrJlBvbVTD82UxIN+B3Vpe4l8G1tb15ocd2RyOQA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache-key@0.84.5: + resolution: {integrity: sha512-3dPB2TnvGjjf0/9O7AXVQURKXuQNauTZE7WpTGTlR017Gh/B5y0m/2wcqxfveUguHSpu89KhVxCAlr2k/H7uhQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache-key@0.84.6: + resolution: {integrity: sha512-6tyXt1BZ/3U183XV48oif7Nm65TE+pTo0Vs31u4FxMgqiYQR/SiSr3RoXn3pmy4pd280ZmLmct0x3jZpu0pV4A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache@0.84.5: + resolution: {integrity: sha512-WHS0n2OxQqtwEjSeQFPePNrMvEFhmQcUQM9cRJMHByWoi/GMWFBEWOf7hVkAM/0KRutAXNbDlSu/cZB6CyxgQQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache@0.84.6: + resolution: {integrity: sha512-KBJVpb02oKNO+jlWQ1Aax7cd11YP0MClDaMgx6C4MhyZ9g95J/dC1Bo6sCzXaU9L9h/w5cQfT5zDkGW5sOj99A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-config@0.84.5: + resolution: {integrity: sha512-zie+uN6oohscowi2S7ByU+wUw6CrT4ZxW9uAbONOObSxx86RGmnIAmjXHLkfmcdYoY7jzOPEbqcI6oeVmqyBQA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-config@0.84.6: + resolution: {integrity: sha512-cD2mLEofcuV26kxSV4hfV1Pbuh6igdUhYQriO8ZVeJ7HyZfq4/wqHV0gSR7fEO8NUp9odr60/Kzm5YakmghWwQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-core@0.84.5: + resolution: {integrity: sha512-xwm605hCi5Y6eJTTb8ZWo6pkUcoBEIyiQOfkZh5GwtDwUrP9SNhTQZhzJHrBCwwxlf3Ptl/pxWJgQ1rsNYMnrA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-core@0.84.6: + resolution: {integrity: sha512-ZqYskM8+f3PFMosBDJd8DLqRvT1ltRGhWL/ZfTpBvf4SoadO30VVrdhAoHFVGz3G1CJpWa3fgZH7nnnX/ybllA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-file-map@0.84.5: + resolution: {integrity: sha512-mlm/JL8toSbSc2akpKIGmzvrVRSCgZ5vkbycI34oMLoOnLGuLyC8WTyVJ6P0hZG/usDaGwZSl/s9BCRriqjGJA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-file-map@0.84.6: + resolution: {integrity: sha512-ov9VywWBsHPt54Zc7/DxWvqjTwxuZvhfeyHg8ZAMGKtf2PcH1wS3EnoBH7In53Rspk/eXVWAO5+avxunPn081w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-minify-terser@0.84.5: + resolution: {integrity: sha512-BJoFwCEDsYnagPqarayInv2+diCDNDdLlaof/p6s9w4gh+gc9HXYM+pDvsKGKKUumpZswNF3Z/ftTMqKl/5IBg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-minify-terser@0.84.6: + resolution: {integrity: sha512-SqhB/Kxrw0XfyW0k8mEscqX/CQopm6Fp3EpdSIPzvKfGgQp9bTMahg9PAdVsXUmIWwErbmFw1VqB+VFBOKksUw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-resolver@0.84.5: + resolution: {integrity: sha512-VSSnepg1k6LyCwtb6eirWdAWlpKwBG8Rdtsr1mU38rMelFyWgh3/QuMSiZIZAIjwg/fsa8GhW5/FO54CAUPCEA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-resolver@0.84.6: + resolution: {integrity: sha512-PplpGc/OCLxgKeLNsLQO/VHsYDIvkcGGr3zNT7UanHCGRqhxKv245o+naV8cRI4/pcLqyc+9j+J0AJbT3Kp5Sg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-runtime@0.84.5: + resolution: {integrity: sha512-U1m2+d1Pr+JO2/iVXBB2OfXXityz7tqwIorxfrT15IEgaHvpJBq/OHiqnOWPKJbUl3JcxjcdviZZOKk85oK4Qg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-runtime@0.84.6: + resolution: {integrity: sha512-47EYO/DOai0PA3GoAyDpyXOgHNMwbl/Z9dQlQineDLHkpD42xOwMrIx1crT0+n6F+aGoEyxZmeFwfMzaIqVqKQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-source-map@0.84.5: + resolution: {integrity: sha512-2BtV5L9uPc49F13Gn5wiP6bX/EncqzqTIk2VL/0F/96Vo0YEOjluT/qktQjFODfqGFsucwnh5mPEAl/2jVEfeg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-source-map@0.84.6: + resolution: {integrity: sha512-4p/EplRbgNxhdqA6kAj1AmIq9qqYXYSAjgxuZ73rYA+p037xDDrxK8CyDKNfbn1k6aMuVt8LV0fpt7NHUTKiZw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-symbolicate@0.84.5: + resolution: {integrity: sha512-rQ40zYDAkaWBN9yvjUuAD0ZpzBMZSoKyGYXnb5JrfbKjun7fTvfoLHL3KXFYenBTYZkQtlp4cKSCv/1utxFyOw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro-symbolicate@0.84.6: + resolution: {integrity: sha512-wJFyyF5ysbVyoYVWGL6GwVMiNnGllwDU2gF096376fl0fc8IFUlADSyNlswj31K9og2sOilXe/FdyWFPczPUYg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro-transform-plugins@0.84.5: + resolution: {integrity: sha512-+InaSVGaOyt0DyRo4Y/zIdPI6CZwnbNho5LAL23tgmuGwv7fyfkF7kKfPjZcfxXBcoYdTLLFnCfCH/dHSiCqNg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-plugins@0.84.6: + resolution: {integrity: sha512-pROPMFaj25Y9+3LlLqpBRz/7B/2YPnfEQz/z3juYxkh3FGQ+3c/Qh4E+khPSTdwTw0M6LitLYHMqaHMbQmuBJw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-worker@0.84.5: + resolution: {integrity: sha512-ui1Z8x4s5RL36gMmKLaMMO7O9NNDHNdthEZSCDQHAau3JcAsTaFOK6I+2q4I/kW5u8hSEjJk9L45TXSVJw6g1A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-worker@0.84.6: + resolution: {integrity: sha512-xLTVrOENaHR9DiuhnHS+zcle0rKsCy2CpXSIx7xD+zhJs+qVRNUyV7Z1zIt/0P/d0cSGVx846R+dIjHEudhcww==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro@0.84.5: + resolution: {integrity: sha512-r1liLkyFZMVSEMNjU1CJU5pRzs3NdkxHqXS60O25c0rCIqAR+cGk7rPydw/g0WAIKVXojIBIF45yYBPagJGcgw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro@0.84.6: + resolution: {integrity: sha512-ty/tx/imE5Ph2gvPU788Ckim6RAC2tyZxLz2hWRgC9U7/fGKV7qY/slADy0qHPEw9cpUgc4BfmV3oUsYwm7oeg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -6028,6 +8007,15 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@1.2.0: + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -6062,6 +8050,19 @@ packages: mlly@1.8.1: resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + module-definition@6.0.2: + resolution: {integrity: sha512-SvAU3lB0+Yjbq55yHY3wkRZBOh+fhU1SnIF3IFbTewv6mtAh7yUT8ACHAJ2mGIJ7tCes2QuCL/cl6m0JSZ/ArA==} + engines: {node: '>=18'} + hasBin: true + + module-lookup-amd@9.1.3: + resolution: {integrity: sha512-Jc3XmOaR9FdfMJSK8+vyLgsCkzm8z2L0NS6vrlRWi12DjS7MY7TMNE7E1yj8yXx837xtMDbKSSgcdXnFlJ2YLg==} + engines: {node: '>=18'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -6075,6 +8076,9 @@ packages: typescript: optional: true + multitars@1.0.2: + resolution: {integrity: sha512-6GwVw5eLi9sThdtlS4PKwC7yRLaf45pYhIEzKBHdKxi+YOXGKFX8acIniH+Uh/+k9mS2lQOupTccjoe5r0/1IQ==} + mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} @@ -6082,8 +8086,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -6099,6 +8103,14 @@ packages: resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} engines: {node: '>=18'} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -6161,10 +8173,23 @@ packages: engines: {node: '>=10.5.0'} deprecated: Use your platform's native DOMException instead + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -6175,10 +8200,18 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-source-walk@7.0.2: + resolution: {integrity: sha512-71kFFjYaSshDTA8/a2HiTYPLdASWjLJxUyJxGE+ffxU+KhxSBtM9kiLUX+R2yooFdSFKMFpi4n3PFtDy6qXv8A==} + engines: {node: '>=18'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + npm-package-arg@11.0.3: + resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} + engines: {node: ^16.14.0 || >=18.0.0} + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -6187,6 +8220,9 @@ packages: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + nwsapi@2.2.23: resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} @@ -6195,6 +8231,14 @@ packages: engines: {node: '>=8.9'} hasBin: true + ob1@0.84.5: + resolution: {integrity: sha512-aH9RkoZc7w/90HBamFxTw8ZLFr05wXS+iOnvmrgo53Ep8Pyrm5FieQSaPIVROkfFVQISeD/zo92fes26TOwe+A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + ob1@0.84.6: + resolution: {integrity: sha512-+s+6zjd0X68hfAcn92NsEPXnSYOF3O7exw/Fj4UfRTB+UL+Tdjdkzu5/4WquXlzqgvNm8FWuuW6/0Yd8S2rYxw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -6234,13 +8278,25 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@2.0.1: + resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} + engines: {node: '>=4'} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -6257,10 +8313,22 @@ packages: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} + open@7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@3.4.0: + resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} + engines: {node: '>=6'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + ora@8.2.0: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} @@ -6325,6 +8393,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@2.1.0: + resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} + engines: {node: '>=6'} + parse-ms@4.0.0: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} @@ -6333,6 +8405,10 @@ packages: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} engines: {node: '>=0.10.0'} + parse-png@2.1.0: + resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} + engines: {node: '>=10'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -6439,10 +8515,18 @@ packages: engines: {node: '>=18'} hasBin: true + plist@3.1.1: + resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} + engines: {node: '>=10.4.0'} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + pngjs@3.4.0: + resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} + engines: {node: '>=4.0.0'} + pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} @@ -6505,14 +8589,25 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.10: - resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + postcss-values-parser@6.0.2: + resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} + engines: {node: '>=10'} + peerDependencies: + postcss: '>=8.5.10' + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} powershell-utils@0.1.0: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} + precinct@12.3.2: + resolution: {integrity: sha512-JbJevI1K80z8e/WIyDt/4vUN/4qcfBSKKqOjJA4mosPPPb7zODKRJQV7YN7apVWN3k58nZYm/vEsLgEGYmnxwg==} + engines: {node: '>=18'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -6530,10 +8625,18 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@30.3.0: resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + pretty-ms@7.0.1: + resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} + engines: {node: '>=10'} + pretty-ms@9.3.0: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} @@ -6542,10 +8645,24 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + proc-log@4.2.0: + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + process-on-spawn@1.1.0: resolution: {integrity: sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==} engines: {node: '>=8'} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + promise@8.3.0: + resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -6568,6 +8685,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} @@ -6583,6 +8703,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quote-unquote@1.0.0: + resolution: {integrity: sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -6591,12 +8714,19 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-day-picker@9.13.2: resolution: {integrity: sha512-IMPiXfXVIAuR5Yk58DDPBC8QKClrhdXV+Tr/alBrwrHUw0qDDYB1m5zPNuTnnPIr/gmJ4ChMxmtqPdxm8+R4Eg==} engines: {node: '>=18'} peerDependencies: react: '>=16.8.0' + react-devtools-core@6.1.5: + resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==} + react-devtools-inline@4.4.0: resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==} @@ -6609,6 +8739,11 @@ packages: resolution: {integrity: sha512-+NRMYs2DyTP4/tqWz371Oo50JqmWltR1h2gcdgUMAWZJIAvrd0/SqlCfx7tpzpl/s36rzw6qH2MjoNrxtRNYhA==} engines: {node: ^20.9.0 || >=22} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -6629,13 +8764,40 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-is@19.2.8: + resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} + react-markdown@10.1.0: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: '@types/react': '>=18' react: '>=18' - react-refresh@0.17.0: + react-native-web@0.21.2: + resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + react-native@0.86.3: + resolution: {integrity: sha512-JR5s3bM9ezud+Mw24GlNXNfthqPIKwrQgPPJcam+L97t2sKjjEavhCzBn+fyqZZRcM5+XlhYxpTxVkK7e1n38Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + peerDependencies: + '@react-native/jest-preset': 0.86.3 + '@types/react': ^19.1.1 + react: ^19.2.3 + peerDependenciesMeta: + '@react-native/jest-preset': + optional: true + '@types/react': + optional: true + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -6681,6 +8843,15 @@ packages: peerDependencies: react: '>= 0.14.0' + react-test-renderer@19.2.3: + resolution: {integrity: sha512-TMR1LnSFiWZMJkCgNf5ATSvAheTT2NvKIwiVwdBPHxjBI7n/JbWd4gaZ16DVd9foAXdvDz+sB5yxZTwMjPRxpw==} + peerDependencies: + react: ^19.2.3 + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + react@19.2.4: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} @@ -6729,6 +8900,16 @@ packages: refractor@5.0.0: resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -6737,6 +8918,13 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + regjsparser@0.13.0: resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true @@ -6778,10 +8966,23 @@ packages: resolution: {integrity: sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==} engines: {node: '>=0.10.5'} + requirejs-config-file@4.0.0: + resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} + engines: {node: '>=10.13.0'} + + requirejs@2.3.8: + resolution: {integrity: sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==} + engines: {node: '>=0.4.0'} + hasBin: true + resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} + resolve-dependency-path@4.0.1: + resolution: {integrity: sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ==} + engines: {node: '>=18'} + resolve-dir@0.1.1: resolution: {integrity: sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==} engines: {node: '>=0.10.0'} @@ -6797,15 +8998,35 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-workspace-root@2.0.1: + resolution: {integrity: sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + restore-cursor@2.0.0: + resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} + engines: {node: '>=4'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -6827,6 +9048,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.63.1: + resolution: {integrity: sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -6862,6 +9088,20 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sandbox-cli-detector@0.2.0: + resolution: {integrity: sha512-4lyHX0ZU0AZKwjgZ1InxZAa3PNpyEb8rOQ+Zss1ReYmhNzW0Q+h1zE5nvniXN0HaAWZaZE1zgVNEirb0R7LmNg==} + engines: {node: '>=18.18'} + hasBin: true + + sass-lookup@6.1.2: + resolution: {integrity: sha512-GjmndmKQBtlPil79RK72L7yc5kDXZPCQeH97bP8R8DcxtXQJO6vECExb3WP/m6+cxaV9h4ZxrSRvCkPG2v/VSw==} + engines: {node: '>=18'} + hasBin: true + + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} + engines: {node: '>=11.0.0'} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -6882,10 +9122,22 @@ packages: engines: {node: '>=10'} hasBin: true + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} + serialize-error@2.1.0: + resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} + engines: {node: '>=0.10.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -6905,6 +9157,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -6924,6 +9179,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -6950,6 +9209,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-plist@1.3.1: + resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -6961,6 +9223,10 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slugify@1.6.9: + resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==} + engines: {node: '>=8.0.0'} + sonner@1.7.4: resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==} peerDependencies: @@ -6974,6 +9240,13 @@ packages: source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -7005,9 +9278,20 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-parser@0.1.11: + resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} + engines: {node: '>=6'} + static-browser-server@1.0.3: resolution: {integrity: sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -7032,6 +9316,13 @@ packages: prettier: optional: true + stream-buffers@2.2.0: + resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} + engines: {node: '>= 0.10.0'} + + stream-to-array@2.3.0: + resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} + strict-event-emitter@0.4.6: resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} @@ -7087,10 +9378,18 @@ packages: stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + stringify-object@5.0.0: resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} engines: {node: '>=14.16'} + strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -7127,10 +9426,17 @@ packages: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + structured-headers@0.4.1: + resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} + style-mod@4.1.3: resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} @@ -7153,6 +9459,14 @@ packages: babel-plugin-macros: optional: true + styleq@0.1.3: + resolution: {integrity: sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==} + + stylus-lookup@6.1.2: + resolution: {integrity: sha512-O+Q/SJ8s1X2aMLh4213fQ9X/bND9M3dhSsyTRe+O1OXPcewGLiYmAtKCrnP7FDvDBaXB2ZHPkCt3zi4cJXBlCQ==} + engines: {node: '>=18'} + hasBin: true + sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} @@ -7170,6 +9484,10 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -7201,6 +9519,19 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + terminal-link@2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + + terser@5.51.2: + resolution: {integrity: sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==} + engines: {node: '>=10'} + hasBin: true + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -7212,6 +9543,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + throat@5.0.0: + resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -7229,6 +9563,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} @@ -7273,6 +9611,9 @@ packages: resolution: {integrity: sha512-fK4DKkEcrpBbK6uANekH37VeNAb/88qKdkqc/nBOFJpHdvXKXdA4lZRkiM6zNlow00Zp4W4/lnWyqqCaOQlg/w==} engines: {node: '>=6', npm: '>=5'} + toqr@0.1.1: + resolution: {integrity: sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==} + tough-cookie@5.1.2: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} @@ -7281,6 +9622,9 @@ packages: resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} @@ -7301,6 +9645,12 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-declaration-location@1.0.7: resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} peerDependencies: @@ -7310,6 +9660,10 @@ packages: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} + ts-graphviz@2.1.6: + resolution: {integrity: sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw==} + engines: {node: '>=18'} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -7393,6 +9747,10 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + type-fest@0.7.1: + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} + type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -7439,6 +9797,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + hasBin: true + ufo@1.6.3: resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} @@ -7454,6 +9816,22 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -7542,6 +9920,15 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@7.0.3: + resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). @@ -7559,6 +9946,10 @@ packages: typescript: optional: true + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + validate-npm-package-name@7.0.2: resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} engines: {node: ^20.17.0 || >=22.9.0} @@ -7700,6 +10091,9 @@ packages: jsdom: optional: true + vlq@1.0.1: + resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} + w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -7717,9 +10111,16 @@ packages: engines: {node: '>=8'} hasBin: true + walkdir@0.4.1: + resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} + engines: {node: '>=6.0.0'} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + weasel-words@0.1.1: resolution: {integrity: sha512-rWkTAGqs4TN6qreS06+irmFUMrQVx5KoFjD8CxMHUsAwmxw/upDcfleaEYOLsonUbornahg+VJ9xrWxp4udyJA==} @@ -7727,6 +10128,9 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -7739,14 +10143,23 @@ packages: engines: {node: '>=18'} deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} + whatwg-url-minimum@0.1.2: + resolution: {integrity: sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==} + whatwg-url@14.2.0: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -7810,6 +10223,10 @@ packages: write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + write-file-atomic@5.0.1: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -7819,8 +10236,20 @@ packages: engines: {node: '>=6', npm: '>=5'} hasBin: true - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@7.5.13: + resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} + 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 + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -7839,13 +10268,29 @@ packages: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} + xcode@3.0.1: + resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} + engines: {node: '>=10.0.0'} + xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + xml2js@0.6.0: + resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} + engines: {node: '>=4.0.0'} + xml@1.0.1: resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -7859,6 +10304,11 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -7942,8 +10392,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.29.0': {} + '@babel/compat-data@7.29.7': {} + '@babel/core@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -7972,10 +10430,22 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.29.0 + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.8 + '@babel/helper-compilation-targets@7.28.6': dependencies: '@babel/compat-data': 7.29.0 @@ -7984,6 +10454,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -7997,8 +10475,41 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.8 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} + '@babel/helper-member-expression-to-functions@7.28.5': dependencies: '@babel/traverse': 7.29.0 @@ -8006,6 +10517,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-imports@7.28.6': dependencies: '@babel/traverse': 7.29.0 @@ -8013,6 +10531,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8026,8 +10551,23 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.8 + '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8037,6 +10577,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: '@babel/traverse': 7.29.0 @@ -8044,12 +10593,33 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helper-wrap-function@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/helpers@7.28.6': dependencies: '@babel/template': 7.28.6 @@ -8059,6 +10629,24 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-export-default-from@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8079,6 +10667,26 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-export-default-from@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-flow@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8099,6 +10707,11 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8144,35 +10757,227 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-flow-strip-types@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.0) + + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.0) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.8(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8188,12 +10993,20 @@ snapshots: '@babel/runtime@7.29.2': {} + '@babel/runtime@7.29.7': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 '@babel/parser': 7.29.0 '@babel/types': 7.29.0 + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -8206,11 +11019,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@0.2.3': {} '@bcoe/v8-coverage@1.0.2': {} @@ -8229,13 +11059,13 @@ snapshots: - eslint-import-resolver-webpack - supports-color - '@chromatic-com/storybook@5.0.1(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@chromatic-com/storybook@5.0.1(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: '@neoconfetti/react': 1.0.0 chromatic: 13.3.5 filesize: 10.1.6 jsonfile: 6.2.0 - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) strip-ansi: 7.1.2 transitivePeerDependencies: - '@chromatic-com/cypress' @@ -8349,6 +11179,15 @@ snapshots: react-dom: 19.2.4(react@19.2.4) react-is: 17.0.2 + '@convex-dev/eslint-plugin@1.1.1(convex@1.31.7(react@19.2.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + convex: 1.31.7(react@19.2.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + '@convex-dev/eslint-plugin@1.1.1(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) @@ -8380,6 +11219,13 @@ snapshots: '@date-fns/tz@1.4.1': {} + '@dependents/detective-less@5.0.3': + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + '@discoveryjs/json-ext@1.1.0': {} + '@dotenvx/dotenvx@1.52.0': dependencies: commander: 11.1.0 @@ -8421,6 +11267,9 @@ snapshots: '@esbuild/aix-ppc64@0.27.3': optional: true + '@esbuild/aix-ppc64@0.27.7': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true @@ -8430,6 +11279,9 @@ snapshots: '@esbuild/android-arm64@0.27.3': optional: true + '@esbuild/android-arm64@0.27.7': + optional: true + '@esbuild/android-arm@0.25.12': optional: true @@ -8439,6 +11291,9 @@ snapshots: '@esbuild/android-arm@0.27.3': optional: true + '@esbuild/android-arm@0.27.7': + optional: true + '@esbuild/android-x64@0.25.12': optional: true @@ -8448,6 +11303,9 @@ snapshots: '@esbuild/android-x64@0.27.3': optional: true + '@esbuild/android-x64@0.27.7': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true @@ -8457,6 +11315,9 @@ snapshots: '@esbuild/darwin-arm64@0.27.3': optional: true + '@esbuild/darwin-arm64@0.27.7': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true @@ -8466,6 +11327,9 @@ snapshots: '@esbuild/darwin-x64@0.27.3': optional: true + '@esbuild/darwin-x64@0.27.7': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true @@ -8475,6 +11339,9 @@ snapshots: '@esbuild/freebsd-arm64@0.27.3': optional: true + '@esbuild/freebsd-arm64@0.27.7': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true @@ -8484,6 +11351,9 @@ snapshots: '@esbuild/freebsd-x64@0.27.3': optional: true + '@esbuild/freebsd-x64@0.27.7': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true @@ -8493,6 +11363,9 @@ snapshots: '@esbuild/linux-arm64@0.27.3': optional: true + '@esbuild/linux-arm64@0.27.7': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true @@ -8502,6 +11375,9 @@ snapshots: '@esbuild/linux-arm@0.27.3': optional: true + '@esbuild/linux-arm@0.27.7': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true @@ -8511,6 +11387,9 @@ snapshots: '@esbuild/linux-ia32@0.27.3': optional: true + '@esbuild/linux-ia32@0.27.7': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true @@ -8520,6 +11399,9 @@ snapshots: '@esbuild/linux-loong64@0.27.3': optional: true + '@esbuild/linux-loong64@0.27.7': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true @@ -8529,6 +11411,9 @@ snapshots: '@esbuild/linux-mips64el@0.27.3': optional: true + '@esbuild/linux-mips64el@0.27.7': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true @@ -8538,6 +11423,9 @@ snapshots: '@esbuild/linux-ppc64@0.27.3': optional: true + '@esbuild/linux-ppc64@0.27.7': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true @@ -8547,6 +11435,9 @@ snapshots: '@esbuild/linux-riscv64@0.27.3': optional: true + '@esbuild/linux-riscv64@0.27.7': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true @@ -8556,6 +11447,9 @@ snapshots: '@esbuild/linux-s390x@0.27.3': optional: true + '@esbuild/linux-s390x@0.27.7': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true @@ -8565,6 +11459,9 @@ snapshots: '@esbuild/linux-x64@0.27.3': optional: true + '@esbuild/linux-x64@0.27.7': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true @@ -8574,6 +11471,9 @@ snapshots: '@esbuild/netbsd-arm64@0.27.3': optional: true + '@esbuild/netbsd-arm64@0.27.7': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true @@ -8583,6 +11483,9 @@ snapshots: '@esbuild/netbsd-x64@0.27.3': optional: true + '@esbuild/netbsd-x64@0.27.7': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true @@ -8592,6 +11495,9 @@ snapshots: '@esbuild/openbsd-arm64@0.27.3': optional: true + '@esbuild/openbsd-arm64@0.27.7': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true @@ -8601,6 +11507,9 @@ snapshots: '@esbuild/openbsd-x64@0.27.3': optional: true + '@esbuild/openbsd-x64@0.27.7': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true @@ -8610,6 +11519,9 @@ snapshots: '@esbuild/openharmony-arm64@0.27.3': optional: true + '@esbuild/openharmony-arm64@0.27.7': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true @@ -8619,6 +11531,9 @@ snapshots: '@esbuild/sunos-x64@0.27.3': optional: true + '@esbuild/sunos-x64@0.27.7': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true @@ -8628,6 +11543,9 @@ snapshots: '@esbuild/win32-arm64@0.27.3': optional: true + '@esbuild/win32-arm64@0.27.7': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true @@ -8637,68 +11555,423 @@ snapshots: '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/win32-x64@0.25.12': - optional: true + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@esbuild/win32-x64@0.27.0': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@1.21.7))': + dependencies: + eslint: 9.39.2(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 10.2.4 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 10.2.4 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@expo/cli@57.0.21(@expo/dom-webview@57.0.1)(expo-constants@57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)))(expo-font@57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3))(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + dependencies: + '@expo/code-signing-certificates': 0.0.6 + '@expo/config': 57.0.9(typescript@5.9.3) + '@expo/config-plugins': 57.0.9(typescript@5.9.3) + '@expo/devcert': 1.2.1 + '@expo/env': 2.4.3 + '@expo/image-utils': 0.11.5(typescript@5.9.3) + '@expo/inline-modules': 0.1.7(typescript@5.9.3) + '@expo/json-file': 11.0.1 + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + '@expo/metro': 56.0.2 + '@expo/metro-config': 57.0.12(expo@57.0.19)(typescript@5.9.3) + '@expo/metro-file-map': 57.0.2 + '@expo/osascript': 2.7.1 + '@expo/package-manager': 1.13.1 + '@expo/plist': 0.8.1 + '@expo/prebuild-config': 57.0.15(typescript@5.9.3) + '@expo/require-utils': 57.0.5(typescript@5.9.3) + '@expo/router-server': 57.0.9(expo-constants@57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)))(expo-font@57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3))(expo-server@57.0.3)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@expo/schema-utils': 57.0.2 + '@expo/spawn-async': 1.8.0 + '@expo/ws-tunnel': 2.0.0(ws@8.21.3) + '@expo/xcpretty': 4.4.4 + '@react-native/dev-middleware': 0.86.3 + accepts: 1.3.8 + agent-cli-detector: 0.1.7 + arg: 5.0.2 + bplist-creator: 0.1.0 + bplist-parser: 0.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + compression: 1.8.1 + connect: 3.7.0 + debug: 4.4.3 + dnssd-advertise: 1.1.6 + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + expo-server: 57.0.3 + fetch-nodeshim: 0.4.10 + getenv: 2.0.0 + glob: 13.0.6 + lan-network: 0.2.1 + multitars: 1.0.2 + node-forge: 1.4.0 + npm-package-arg: 11.0.3 + ora: 3.4.0 + picomatch: 4.0.4 + pretty-format: 29.7.0 + progress: 2.0.3 + prompts: 2.4.2 + resolve-from: 5.0.0 + sandbox-cli-detector: 0.2.0 + semver: 7.7.4 + send: 0.19.2 + slugify: 1.6.9 + stacktrace-parser: 0.1.11 + structured-headers: 0.4.1 + terminal-link: 2.1.1 + toqr: 0.1.1 + wrap-ansi: 7.0.0 + ws: 8.21.3 + zod: 3.25.76 + optionalDependencies: + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + transitivePeerDependencies: + - '@expo/dom-webview' + - '@expo/metro-runtime' + - bufferutil + - expo-constants + - expo-font + - react + - react-dom + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + + '@expo/code-signing-certificates@0.0.6': + dependencies: + node-forge: 1.4.0 + + '@expo/config-plugins@57.0.9(typescript@5.9.3)': + dependencies: + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/plist': 0.8.1 + '@expo/require-utils': 57.0.5(typescript@5.9.3) + '@expo/sdk-runtime-versions': 1.0.0 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + semver: 7.7.4 + slugify: 1.6.9 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/config-types@57.0.2': {} + + '@expo/config@57.0.9(typescript@5.9.3)': + dependencies: + '@expo/config-plugins': 57.0.9(typescript@5.9.3) + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/require-utils': 57.0.5(typescript@5.9.3) + deepmerge: 4.3.1 + getenv: 2.0.0 + glob: 13.0.6 + resolve-workspace-root: 2.0.1 + semver: 7.7.4 + slugify: 1.6.9 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/devcert@1.2.1': + dependencies: + '@expo/sudo-prompt': 9.3.2 + debug: 3.2.7 + transitivePeerDependencies: + - supports-color + + '@expo/devtools@57.0.1(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)': + dependencies: + chalk: 4.1.2 + optionalDependencies: + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + '@expo/dom-webview@57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)': + dependencies: + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + '@expo/env@2.4.3': + dependencies: + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + transitivePeerDependencies: + - supports-color + + '@expo/expo-modules-macros-plugin@0.6.1': {} + + '@expo/fingerprint@0.20.12': + dependencies: + '@expo/env': 2.4.3 + '@expo/spawn-async': 1.8.0 + arg: 5.0.2 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + ignore: 5.3.2 + minimatch: 10.2.4 + resolve-from: 5.0.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + '@expo/image-utils@0.11.5(typescript@5.9.3)': + dependencies: + '@expo/require-utils': 57.0.5(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + getenv: 2.0.0 + jimp-compact: 0.16.1 + parse-png: 2.1.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/inline-modules@0.1.7(typescript@5.9.3)': + dependencies: + '@expo/config-plugins': 57.0.9(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/json-file@11.0.1': + dependencies: + '@babel/code-frame': 7.29.0 + json5: 2.2.3 + + '@expo/local-build-cache-provider@57.0.8(typescript@5.9.3)': + dependencies: + '@expo/config': 57.0.9(typescript@5.9.3) + chalk: 4.1.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/log-box@57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)': + dependencies: + '@expo/dom-webview': 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + anser: 1.4.10 + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + stacktrace-parser: 0.1.11 + + '@expo/metro-config@57.0.12(expo@57.0.19)(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@expo/config': 57.0.9(typescript@5.9.3) + '@expo/env': 2.4.3 + '@expo/json-file': 11.0.1 + '@expo/metro': 56.0.2 + '@expo/require-utils': 57.0.5(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + browserslist: 4.28.1 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + hermes-parser: 0.36.1 + jsc-safe-url: 0.2.4 + lightningcss: 1.33.0 + picomatch: 4.0.4 + postcss: 8.5.28 + resolve-from: 5.0.0 + optionalDependencies: + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + '@expo/metro-file-map@57.0.2': + dependencies: + debug: 4.4.3 + fb-watchman: 2.0.2 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color - '@esbuild/win32-x64@0.27.0': - optional: true + '@expo/metro@56.0.2': + dependencies: + metro: 0.84.5 + metro-babel-transformer: 0.84.5 + metro-cache: 0.84.5 + metro-cache-key: 0.84.5 + metro-config: 0.84.5 + metro-core: 0.84.5 + metro-file-map: 0.84.5 + metro-minify-terser: 0.84.5 + metro-resolver: 0.84.5 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5 + metro-symbolicate: 0.84.5 + metro-transform-plugins: 0.84.5 + metro-transform-worker: 0.84.5 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - '@esbuild/win32-x64@0.27.3': - optional: true + '@expo/osascript@2.7.1': + dependencies: + '@expo/spawn-async': 1.8.0 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@1.21.7))': + '@expo/package-manager@1.13.1': dependencies: - eslint: 9.39.2(jiti@1.21.7) - eslint-visitor-keys: 3.4.3 + '@expo/json-file': 11.0.1 + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + npm-package-arg: 11.0.3 + ora: 3.4.0 + resolve-workspace-root: 2.0.1 - '@eslint-community/regexpp@4.12.2': {} + '@expo/plist@0.8.1': + dependencies: + '@xmldom/xmldom': 0.8.15 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 - '@eslint/config-array@0.21.1': + '@expo/prebuild-config@57.0.15(typescript@5.9.3)': dependencies: - '@eslint/object-schema': 2.1.7 + '@expo/config': 57.0.9(typescript@5.9.3) + '@expo/config-plugins': 57.0.9(typescript@5.9.3) + '@expo/config-types': 57.0.2 + '@expo/image-utils': 0.11.5(typescript@5.9.3) + '@expo/json-file': 11.0.1 + '@react-native/normalize-colors': 0.86.3 debug: 4.4.3 - minimatch: 10.2.4 + expo-modules-autolinking: 57.0.12(typescript@5.9.3) + resolve-from: 5.0.0 + semver: 7.7.4 transitivePeerDependencies: - supports-color + - typescript - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 - - '@eslint/core@0.17.0': + '@expo/require-utils@57.0.5(typescript@5.9.3)': dependencies: - '@types/json-schema': 7.0.15 + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@eslint/eslintrc@3.3.3': + '@expo/router-server@57.0.9(expo-constants@57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)))(expo-font@57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3))(expo-server@57.0.3)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - ajv: 6.14.0 debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 10.2.4 - strip-json-comments: 3.1.1 + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + expo-constants: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)) + expo-font: 57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + expo-server: 57.0.3 + react: 19.2.3 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - supports-color - '@eslint/js@9.39.2': {} + '@expo/schema-utils@57.0.2': {} - '@eslint/object-schema@2.1.7': {} + '@expo/sdk-runtime-versions@1.0.0': {} - '@eslint/plugin-kit@0.4.1': + '@expo/spawn-async@1.8.0': dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 + cross-spawn: 7.0.6 + + '@expo/sudo-prompt@9.3.2': {} + + '@expo/ws-tunnel@2.0.0(ws@8.21.3)': + dependencies: + ws: 8.21.3 + + '@expo/xcpretty@4.4.4': + dependencies: + '@babel/code-frame': 7.29.0 + chalk: 4.1.2 + js-yaml: 4.1.1 '@figspec/components@2.1.0': {} - '@figspec/react@2.0.1(@types/react@19.2.13)(react@19.2.4)': + '@figspec/react@2.0.1(@types/react@19.2.13)(react@19.2.3)': dependencies: '@figspec/components': 2.1.0 '@lit-labs/react': 2.1.3(@types/react@19.2.13) - react: 19.2.4 + react: 19.2.3 transitivePeerDependencies: - '@types/react' @@ -8711,11 +11984,11 @@ snapshots: '@floating-ui/core': 1.7.4 '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@floating-ui/react-dom@2.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@floating-ui/dom': 1.7.5 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) '@floating-ui/utils@0.2.10': {} @@ -8731,13 +12004,13 @@ snapshots: dependencies: '@formatjs/fast-memoize': 3.1.6 - '@github-ui/storybook-addon-performance-panel@1.1.4(@storybook/icons@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@storybook/react@10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@github-ui/storybook-addon-performance-panel@1.1.4(@storybook/icons@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@storybook/react@10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: - '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: - '@storybook/react': 10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) - react: 19.2.4 + '@storybook/react': 10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) + react: 19.2.3 '@hapi/hoek@9.3.0': {} @@ -8749,10 +12022,10 @@ snapshots: dependencies: hono: 4.12.18 - '@hookform/resolvers@5.2.2(react-hook-form@7.73.1(react@19.2.4))': + '@hookform/resolvers@5.2.2(react-hook-form@7.73.1(react@19.2.3))': dependencies: '@standard-schema/utils': 0.3.0 - react-hook-form: 7.73.1(react@19.2.4) + react-hook-form: 7.73.1(react@19.2.3) '@humanfs/core@0.19.1': {} @@ -8899,6 +12172,8 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/ttlcache@1.4.1': {} + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -8909,6 +12184,15 @@ snapshots: '@istanbuljs/schema@0.1.3': {} + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + '@jest/console@30.3.0': dependencies: '@jest/types': 30.3.0 @@ -8918,6 +12202,41 @@ snapshots: jest-util: 30.3.0 slash: 3.0.0 + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.19.10) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + '@jest/core@30.3.0': dependencies: '@jest/console': 30.3.0 @@ -8953,12 +12272,24 @@ snapshots: - supports-color - ts-node + '@jest/create-cache-key-function@29.7.0': + dependencies: + '@jest/types': 29.6.3 + optional: true + '@jest/create-cache-key-function@30.3.0': dependencies: '@jest/types': 30.3.0 '@jest/diff-sequences@30.3.0': {} + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + jest-mock: 29.7.0 + '@jest/environment@30.3.0': dependencies: '@jest/fake-timers': 30.3.0 @@ -8966,10 +12297,21 @@ snapshots: '@types/node': 22.19.10 jest-mock: 30.3.0 + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + '@jest/expect-utils@30.3.0': dependencies: '@jest/get-type': 30.1.0 + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + '@jest/expect@30.3.0': dependencies: expect: 30.3.0 @@ -8977,6 +12319,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 22.19.10 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + '@jest/fake-timers@30.3.0': dependencies: '@jest/types': 30.3.0 @@ -8988,6 +12339,15 @@ snapshots: '@jest/get-type@30.1.0': {} + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + '@jest/globals@30.3.0': dependencies: '@jest/environment': 30.3.0 @@ -9002,6 +12362,35 @@ snapshots: '@types/node': 22.19.10 jest-regex-util: 30.0.1 + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 22.19.10 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + '@jest/reporters@30.3.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -9030,6 +12419,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.12 + '@jest/schemas@30.0.5': dependencies: '@sinclair/typebox': 0.34.48 @@ -9041,12 +12434,25 @@ snapshots: graceful-fs: 4.2.11 natural-compare: 1.4.0 + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + '@jest/source-map@30.0.1': dependencies: '@jridgewell/trace-mapping': 0.3.31 callsites: 3.1.0 graceful-fs: 4.2.11 + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + '@jest/test-result@30.3.0': dependencies: '@jest/console': 30.3.0 @@ -9054,6 +12460,13 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.3 + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + '@jest/test-sequencer@30.3.0': dependencies: '@jest/test-result': 30.3.0 @@ -9061,6 +12474,26 @@ snapshots: jest-haste-map: 30.3.0 slash: 3.0.0 + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + '@jest/transform@30.3.0': dependencies: '@babel/core': 7.29.0 @@ -9080,6 +12513,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.19.10 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + '@jest/types@30.3.0': dependencies: '@jest/pattern': 30.0.1 @@ -9090,11 +12532,11 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: glob: 13.0.6 react-docgen-typescript: 2.4.0(typescript@5.9.3) - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) optionalDependencies: typescript: 5.9.3 @@ -9110,6 +12552,11 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': @@ -9187,11 +12634,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@mdx-js/react@3.1.1(@types/react@19.2.13)(react@19.2.3)': + dependencies: + '@types/mdx': 2.0.13 + '@types/react': 19.2.13 + react: 19.2.3 + '@mdx-js/react@3.1.1(@types/react@19.2.13)(react@19.2.4)': dependencies: '@types/mdx': 2.0.13 '@types/react': 19.2.13 react: 19.2.4 + optional: true '@modelcontextprotocol/sdk@1.26.0(zod@3.25.76)': dependencies: @@ -9224,6 +12678,9 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.8.1 @@ -9384,11 +12841,11 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/experimental-ct-core@1.58.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)': + '@playwright/experimental-ct-core@1.58.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)': dependencies: playwright: 1.58.2 playwright-core: 1.58.2 - vite: 6.4.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 6.4.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -9402,10 +12859,10 @@ snapshots: - tsx - yaml - '@playwright/experimental-ct-react@1.58.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@playwright/experimental-ct-react@1.58.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))(yaml@2.9.0)': dependencies: - '@playwright/experimental-ct-core': 1.58.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) - '@vitejs/plugin-react': 4.7.0(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + '@playwright/experimental-ct-core': 1.58.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) + '@vitejs/plugin-react': 4.7.0(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - '@types/node' - jiti @@ -9429,628 +12886,628 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-aspect-ratio@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-aspect-ratio@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-avatar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-avatar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-context': 1.1.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-context': 1.1.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-context@1.1.2(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-context@1.1.3(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-context@1.1.3(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) aria-hidden: 1.2.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-id@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) aria-hidden: 1.2.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) aria-hidden: 1.2.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@floating-ui/react-dom': 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.4) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/react-dom': 2.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.3) '@radix-ui/rect': 1.1.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) aria-hidden: 1.2.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.13)(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 - use-sync-external-store: 1.6.0(react@19.2.4) + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: '@radix-ui/rect': 1.1.1 - react: 19.2.4 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.13)(react@19.2.4)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.13)(react@19.2.3)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.4) - react: 19.2.4 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.13)(react@19.2.3) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.13 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) @@ -10067,91 +13524,297 @@ snapshots: dependencies: react: 19.2.4 + '@react-native/assets-registry@0.86.3': {} + + '@react-native/babel-plugin-codegen@0.86.3(@babel/core@7.29.0)': + dependencies: + '@babel/traverse': 7.29.8 + '@react-native/codegen': 0.86.3(@babel/core@7.29.0) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + '@react-native/babel-preset@0.86.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.8(@babel/core@7.29.0) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.0) + '@react-native/babel-plugin-codegen': 0.86.3(@babel/core@7.29.0) + babel-plugin-syntax-hermes-parser: 0.36.0 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + react-refresh: 0.14.2 + transitivePeerDependencies: + - supports-color + + '@react-native/codegen@0.86.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + hermes-parser: 0.36.0 + invariant: 2.2.4 + nullthrows: 1.1.1 + tinyglobby: 0.2.15 + yargs: 17.7.2 + + '@react-native/community-cli-plugin@0.86.3': + dependencies: + '@react-native/dev-middleware': 0.86.3 + debug: 4.4.3 + invariant: 2.2.4 + metro: 0.84.6 + metro-config: 0.84.6 + metro-core: 0.84.6 + semver: 7.7.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/debugger-frontend@0.86.3': {} + + '@react-native/debugger-shell@0.86.3': + dependencies: + cross-spawn: 7.0.6 + debug: 4.4.3 + fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color + + '@react-native/dev-middleware@0.86.3': + dependencies: + '@isaacs/ttlcache': 1.4.1 + '@react-native/debugger-frontend': 0.86.3 + '@react-native/debugger-shell': 0.86.3 + chrome-launcher: 0.15.2 + chromium-edge-launcher: 0.3.0 + connect: 3.7.0 + debug: 4.4.3 + invariant: 2.2.4 + nullthrows: 1.1.1 + open: 7.4.2 + serve-static: 1.16.3 + ws: 7.5.13 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/gradle-plugin@0.86.3': {} + + '@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3)': + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@react-native/js-polyfills': 0.86.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + jest-environment-node: 29.7.0 + react: 19.2.3 + regenerator-runtime: 0.13.11 + transitivePeerDependencies: + - '@babel/core' + - supports-color + optional: true + + '@react-native/js-polyfills@0.86.3': {} + + '@react-native/normalize-colors@0.74.89': {} + + '@react-native/normalize-colors@0.86.3': {} + + '@react-native/virtualized-lists@0.86.3(@types/react@19.2.13)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)': + dependencies: + invariant: 2.2.4 + nullthrows: 1.1.1 + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.13 + '@rolldown/pluginutils@1.0.0-beta.27': {} - '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + '@rollup/pluginutils@5.3.0(rollup@4.63.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.4 optionalDependencies: - rollup: 4.59.0 + rollup: 4.63.1 '@rollup/rollup-android-arm-eabi@4.59.0': optional: true + '@rollup/rollup-android-arm-eabi@4.63.1': + optional: true + '@rollup/rollup-android-arm64@4.59.0': optional: true + '@rollup/rollup-android-arm64@4.63.1': + optional: true + '@rollup/rollup-darwin-arm64@4.59.0': optional: true + '@rollup/rollup-darwin-arm64@4.63.1': + optional: true + '@rollup/rollup-darwin-x64@4.59.0': optional: true + '@rollup/rollup-darwin-x64@4.63.1': + optional: true + '@rollup/rollup-freebsd-arm64@4.59.0': optional: true + '@rollup/rollup-freebsd-arm64@4.63.1': + optional: true + '@rollup/rollup-freebsd-x64@4.59.0': optional: true + '@rollup/rollup-freebsd-x64@4.63.1': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.59.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.63.1': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.59.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.63.1': + optional: true + '@rollup/rollup-linux-arm64-musl@4.59.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.63.1': + optional: true + '@rollup/rollup-linux-loong64-gnu@4.59.0': optional: true + '@rollup/rollup-linux-loong64-gnu@4.63.1': + optional: true + '@rollup/rollup-linux-loong64-musl@4.59.0': optional: true + '@rollup/rollup-linux-loong64-musl@4.63.1': + optional: true + '@rollup/rollup-linux-ppc64-gnu@4.59.0': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.63.1': + optional: true + '@rollup/rollup-linux-ppc64-musl@4.59.0': optional: true + '@rollup/rollup-linux-ppc64-musl@4.63.1': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.59.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.63.1': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.59.0': optional: true + '@rollup/rollup-linux-riscv64-musl@4.63.1': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.59.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.63.1': + optional: true + '@rollup/rollup-linux-x64-gnu@4.59.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.63.1': + optional: true + '@rollup/rollup-linux-x64-musl@4.59.0': optional: true + '@rollup/rollup-linux-x64-musl@4.63.1': + optional: true + '@rollup/rollup-openbsd-x64@4.59.0': optional: true + '@rollup/rollup-openbsd-x64@4.63.1': + optional: true + '@rollup/rollup-openharmony-arm64@4.59.0': optional: true + '@rollup/rollup-openharmony-arm64@4.63.1': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.59.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.63.1': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.59.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.63.1': + optional: true + '@rollup/rollup-win32-x64-gnu@4.59.0': optional: true + '@rollup/rollup-win32-x64-gnu@4.63.1': + optional: true + '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.63.1': + optional: true + '@schummar/icu-type-parser@1.21.5': {} '@sec-ant/readable-stream@0.4.1': {} @@ -10164,6 +13827,8 @@ snapshots: '@sideway/pinpoint@2.0.0': {} + '@sinclair/typebox@0.27.12': {} + '@sinclair/typebox@0.34.48': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -10172,6 +13837,10 @@ snapshots: dependencies: type-detect: 4.0.8 + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers@15.1.1': dependencies: '@sinonjs/commons': 3.0.1 @@ -10182,32 +13851,32 @@ snapshots: '@stitches/core@1.2.8': {} - '@storybook/addon-a11y@10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@storybook/addon-a11y@10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.11.1 - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@storybook/addon-designs@11.1.2(@storybook/addon-docs@10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@storybook/addon-designs@11.1.2(@storybook/addon-docs@10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: - '@figspec/react': 2.0.1(@types/react@19.2.13)(react@19.2.4) - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@figspec/react': 2.0.1(@types/react@19.2.13)(react@19.2.3) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: - '@storybook/addon-docs': 10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@storybook/addon-docs': 10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - '@types/react' - '@storybook/addon-docs@10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@storybook/addon-docs@10.2.17(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@mdx-js/react': 3.1.1(@types/react@19.2.13)(react@19.2.4) - '@storybook/csf-plugin': 10.2.17(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) - '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@storybook/react-dom-shim': 10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@mdx-js/react': 3.1.1(@types/react@19.2.13)(react@19.2.3) + '@storybook/csf-plugin': 10.2.17(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + '@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@storybook/react-dom-shim': 10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' @@ -10216,50 +13885,50 @@ snapshots: - vite - webpack - '@storybook/addon-mcp@0.3.4(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)': + '@storybook/addon-mcp@0.3.4(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': dependencies: '@storybook/mcp': 0.5.1(typescript@5.9.3) '@tmcp/adapter-valibot': 0.1.5(tmcp@1.19.3(typescript@5.9.3))(valibot@1.2.0(typescript@5.9.3)) '@tmcp/transport-http': 0.8.5(tmcp@1.19.3(typescript@5.9.3)) picoquery: 2.5.0 - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tmcp: 1.19.3(typescript@5.9.3) valibot: 1.2.0(typescript@5.9.3) transitivePeerDependencies: - '@tmcp/auth' - typescript - '@storybook/addon-themes@10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@storybook/addon-themes@10.2.17(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 - '@storybook/builder-vite@10.2.17(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@storybook/builder-vite@10.2.17(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@storybook/csf-plugin': 10.2.17(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@storybook/csf-plugin': 10.2.17(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - esbuild - rollup - webpack - '@storybook/csf-plugin@10.2.17(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@storybook/csf-plugin@10.2.17(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.3 - rollup: 4.59.0 - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + rollup: 4.63.1 + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) '@storybook/global@5.0.0': {} - '@storybook/icons@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@storybook/icons@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) '@storybook/mcp@0.5.1(typescript@5.9.3)': dependencies: @@ -10271,27 +13940,27 @@ snapshots: - '@tmcp/auth' - typescript - '@storybook/react-dom-shim@10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@storybook/react-dom-shim@10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@storybook/react-vite@10.2.17(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@storybook/react-vite@10.2.17(esbuild@0.27.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.4(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) - '@storybook/builder-vite': 10.2.17(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) - '@storybook/react': 10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.4(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + '@rollup/pluginutils': 5.3.0(rollup@4.63.1) + '@storybook/builder-vite': 10.2.17(esbuild@0.27.3)(rollup@4.63.1)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) + '@storybook/react': 10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) empathic: 2.0.0 magic-string: 0.30.21 - react: 19.2.4 + react: 19.2.3 react-docgen: 8.0.2 - react-dom: 19.2.4(react@19.2.4) + react-dom: 19.2.3(react@19.2.3) resolve: 1.22.11 - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tsconfig-paths: 4.2.0 - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - esbuild - rollup @@ -10299,20 +13968,20 @@ snapshots: - typescript - webpack - '@storybook/react@10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)': + '@storybook/react@10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.2.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) - react: 19.2.4 + '@storybook/react-dom-shim': 10.2.17(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + react: 19.2.3 react-docgen: 8.0.2 - react-dom: 19.2.4(react@19.2.4) - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react-dom: 19.2.3(react@19.2.3) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@storybook/test-runner@0.24.2(@types/node@22.19.10)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@storybook/test-runner@0.24.2(@types/node@22.19.10)(storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 @@ -10334,7 +14003,7 @@ snapshots: playwright: 1.58.2 playwright-core: 1.58.2 rimraf: 3.0.2 - storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) uuid: 8.3.2 transitivePeerDependencies: - '@swc/helpers' @@ -10409,23 +14078,23 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19(tsx@4.21.0))': + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0))': dependencies: postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.19(tsx@4.21.0) + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.9.0) - '@tanstack/react-table@8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-table@8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@tanstack/table-core': 8.21.3 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) '@tanstack/table-core@8.21.3': {} '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -10442,12 +14111,24 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@22.19.10))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + jest-matcher-utils: 30.3.0 + picocolors: 1.1.1 + pretty-format: 30.3.0 + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + react-test-renderer: 19.2.3(react@19.2.3) + redent: 3.0.0 + optionalDependencies: + jest: 29.7.0(@types/node@22.19.10) + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.6 '@testing-library/dom': 10.4.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) @@ -10473,6 +14154,21 @@ snapshots: esm-env: 1.2.2 tmcp: 1.19.3(typescript@5.9.3) + '@ts-graphviz/adapter@2.0.6': + dependencies: + '@ts-graphviz/common': 2.1.5 + + '@ts-graphviz/ast@2.0.7': + dependencies: + '@ts-graphviz/common': 2.1.5 + + '@ts-graphviz/common@2.1.5': {} + + '@ts-graphviz/core@2.0.7': + dependencies: + '@ts-graphviz/ast': 2.0.7 + '@ts-graphviz/common': 2.1.5 + '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 @@ -10547,6 +14243,12 @@ snapshots: '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 22.19.10 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -10561,6 +14263,11 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + '@types/json-schema@7.0.15': {} '@types/mdast@4.0.4': @@ -10670,6 +14377,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.69.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@5.9.3) + '@typescript-eslint/types': 8.69.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.49.0': dependencies: '@typescript-eslint/types': 8.49.0 @@ -10697,6 +14413,10 @@ snapshots: dependencies: typescript: 5.9.3 + '@typescript-eslint/tsconfig-utils@8.69.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + '@typescript-eslint/type-utils@8.55.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.55.0 @@ -10727,6 +14447,8 @@ snapshots: '@typescript-eslint/types@8.56.1': {} + '@typescript-eslint/types@8.69.0': {} + '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) @@ -10772,6 +14494,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.69.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.69.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@5.9.3) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/visitor-keys': 8.69.0 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7)) @@ -10820,6 +14557,11 @@ snapshots: '@typescript-eslint/types': 8.56.1 eslint-visitor-keys: 5.0.1 + '@typescript-eslint/visitor-keys@8.69.0': + dependencies: + '@typescript-eslint/types': 8.69.0 + eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.0': {} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -10885,17 +14627,17 @@ snapshots: dependencies: valibot: 1.2.0(typescript@5.9.3) - '@vercel/analytics@1.6.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + '@vercel/analytics@1.6.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': optionalDependencies: - next: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 - '@vercel/speed-insights@1.3.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + '@vercel/speed-insights@1.3.1(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': optionalDependencies: - next: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 - '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -10903,7 +14645,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -10919,7 +14661,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + vitest: 4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/expect@3.2.4': dependencies: @@ -10938,14 +14680,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0))': + '@vitest/mocker@4.1.9(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.9(@types/node@22.19.10)(typescript@5.9.3) - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -10985,7 +14727,39 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@vllnt/eslint-config@1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3)': + '@vllnt/eslint-config@1.0.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(convex@1.31.7(react@19.2.3))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3)': + dependencies: + '@convex-dev/eslint-plugin': 1.1.1(convex@1.31.7(react@19.2.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint/js': 9.39.2 + '@next/eslint-plugin-next': 16.1.6 + eslint: 9.39.2(jiti@1.21.7) + eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-boundaries: 5.4.0(@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-css-modules: 2.12.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-functional: 9.0.2(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-perfectionist: 5.6.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1) + eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-simple-import-sort: 12.1.1(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-turbo: 2.8.13(eslint@9.39.2(jiti@1.21.7))(turbo@2.8.3) + eslint-plugin-unicorn: 62.0.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-write-good-comments: 0.2.0 + prettier: 3.8.1 + typescript-eslint: 8.56.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/eslint' + - '@typescript-eslint/parser' + - convex + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - turbo + + '@vllnt/eslint-config@1.0.0(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(prettier@3.8.1)(turbo@2.8.3)(typescript@5.9.3)': dependencies: '@convex-dev/eslint-plugin': 1.1.1(convex@1.31.7(react@19.2.4))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@eslint/js': 9.39.2 @@ -11017,17 +14791,53 @@ snapshots: - supports-color - turbo - '@vllnt/next-llms@0.1.0-canary.78c9be3': {} + '@vllnt/next-llms@0.1.0-canary.bfc9152': {} '@vllnt/typescript@1.0.0': {} - '@xyflow/react@12.10.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@vue/compiler-core@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.42 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.42': + dependencies: + '@vue/compiler-core': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/compiler-sfc@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.42 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-ssr': 3.5.42 + '@vue/shared': 3.5.42 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.28 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.42': + dependencies: + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/shared@3.5.42': {} + + '@xmldom/xmldom@0.8.15': {} + + '@xmldom/xmldom@0.9.12': {} + + '@xyflow/react@12.10.0(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@xyflow/system': 0.0.74 classcat: 5.0.5 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - zustand: 4.5.7(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + zustand: 4.5.7(@types/react@19.2.13)(react@19.2.3) transitivePeerDependencies: - '@types/react' - immer @@ -11044,6 +14854,15 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -11061,6 +14880,8 @@ snapshots: agent-base@7.1.4: {} + agent-cli-detector@0.1.7: {} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 @@ -11084,6 +14905,8 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + anser@1.4.10: {} + anser@2.3.5: {} ansi-escapes@4.3.2: @@ -11094,6 +14917,8 @@ snapshots: dependencies: environment: 1.1.0 + ansi-regex@4.1.1: {} + ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -11117,6 +14942,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + app-module-path@2.2.0: {} + append-transform@2.0.0: dependencies: default-require-extensions: 3.0.1 @@ -11198,8 +15025,12 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + asap@2.0.6: {} + assertion-error@2.0.1: {} + ast-module-types@6.0.2: {} + ast-types-flow@0.0.8: {} ast-types@0.16.1: @@ -11218,13 +15049,13 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.24(postcss@8.5.10): + autoprefixer@10.4.24(postcss@8.5.28): dependencies: browserslist: 4.28.1 caniuse-lite: 1.0.30001769 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.10 + postcss: 8.5.28 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -11243,6 +15074,19 @@ snapshots: axobject-query@4.1.0: {} + babel-jest@29.7.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + babel-jest@30.3.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -11256,6 +15100,16 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + babel-plugin-istanbul@7.0.1: dependencies: '@babel/helper-plugin-utils': 7.28.6 @@ -11266,10 +15120,61 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + babel-plugin-jest-hoist@30.3.0: dependencies: '@types/babel__core': 7.20.5 + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.0 + + babel-plugin-react-native-web@0.21.2: {} + + babel-plugin-syntax-hermes-parser@0.36.0: + dependencies: + hermes-parser: 0.36.0 + + babel-plugin-syntax-hermes-parser@0.36.1: + dependencies: + hermes-parser: 0.36.1 + + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0): + dependencies: + '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.0) + transitivePeerDependencies: + - '@babel/core' + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -11289,6 +15194,64 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + babel-preset-expo@57.0.10(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@57.0.19)(react-refresh@0.14.2): + dependencies: + '@babel/generator': 7.29.1 + '@babel/helper-module-imports': 7.28.6 + '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@react-native/babel-plugin-codegen': 0.86.3(@babel/core@7.29.0) + babel-plugin-react-compiler: 1.0.0 + babel-plugin-react-native-web: 0.21.2 + babel-plugin-syntax-hermes-parser: 0.36.1 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + debug: 4.4.3 + react-refresh: 0.14.2 + optionalDependencies: + '@babel/runtime': 7.29.2 + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + babel-preset-jest@29.6.3(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-jest@30.3.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -11305,8 +15268,16 @@ snapshots: baseline-browser-mapping@2.9.19: {} + big-integer@1.6.52: {} + binary-extensions@2.3.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + body-parser@2.2.2: dependencies: bytes: 3.1.2 @@ -11321,6 +15292,18 @@ snapshots: transitivePeerDependencies: - supports-color + bplist-creator@0.1.0: + dependencies: + stream-buffers: 2.2.0 + + bplist-parser@0.3.1: + dependencies: + big-integer: 1.6.52 + + bplist-parser@0.3.2: + dependencies: + big-integer: 1.6.52 + brace-expansion@1.1.13: dependencies: balanced-match: 1.0.2 @@ -11348,6 +15331,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -11459,8 +15447,33 @@ snapshots: chromatic@13.3.5: {} + chrome-launcher@0.15.2: + dependencies: + '@types/node': 22.19.10 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + transitivePeerDependencies: + - supports-color + + chromium-edge-launcher@0.3.0: + dependencies: + '@types/node': 22.19.10 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + mkdirp: 1.0.4 + transitivePeerDependencies: + - supports-color + + ci-info@2.0.0: {} + + ci-info@3.9.0: {} + ci-info@4.4.0: {} + cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.0: {} class-variance-authority@0.7.1: @@ -11477,6 +15490,14 @@ snapshots: clean-stack@2.2.0: {} + cli-cursor@2.1.0: + dependencies: + restore-cursor: 2.0.0 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -11499,16 +15520,18 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clone@1.0.4: {} + clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.13)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -11551,12 +15574,39 @@ snapshots: commander@4.1.1: {} + commander@7.2.0: {} + commondir@1.0.1: {} + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + concat-map@0.0.1: {} confbox@0.1.8: {} + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + consola@3.4.2: {} content-disposition@1.0.1: {} @@ -11567,6 +15617,13 @@ snapshots: convert-source-map@2.0.0: {} + convex@1.31.7(react@19.2.3): + dependencies: + esbuild: 0.27.0 + prettier: 3.8.1 + optionalDependencies: + react: 19.2.3 + convex@1.31.7(react@19.2.4): dependencies: esbuild: 0.27.0 @@ -11598,14 +15655,39 @@ snapshots: optionalDependencies: typescript: 5.9.3 + create-jest@29.7.0(@types/node@22.19.10): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.19.10) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + crelt@1.0.6: {} + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + css-in-js-utils@3.1.0: + dependencies: + hyphenate-style-name: 1.1.0 + css.escape@1.5.1: {} cssesc@3.0.0: {} @@ -11694,6 +15776,10 @@ snapshots: date-fns@4.1.0: {} + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@3.2.7: dependencies: ms: 2.1.3 @@ -11714,6 +15800,8 @@ snapshots: deep-eql@5.0.2: {} + deep-extend@0.6.0: {} + deep-is@0.1.4: {} deepmerge-ts@7.1.5: {} @@ -11731,6 +15819,10 @@ snapshots: dependencies: strip-bom: 4.0.0 + defaults@1.0.4: + dependencies: + clone: 1.0.4 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -11749,20 +15841,90 @@ snapshots: depd@2.0.0: {} + dependency-tree@11.5.0: + dependencies: + '@discoveryjs/json-ext': 1.1.0 + commander: 12.1.0 + filing-cabinet: 5.5.1 + precinct: 12.3.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + dequal@2.0.3: {} + destroy@1.2.0: {} + detect-libc@2.1.2: {} detect-newline@3.1.0: {} detect-node-es@1.1.0: {} + detective-amd@6.1.0: + dependencies: + ast-module-types: 6.0.2 + escodegen: 2.1.0 + get-amd-module-type: 6.0.2 + node-source-walk: 7.0.2 + + detective-cjs@6.1.1: + dependencies: + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + + detective-es6@5.0.2: + dependencies: + node-source-walk: 7.0.2 + + detective-postcss@8.0.4(postcss@8.5.28): + dependencies: + is-url-superb: 4.0.0 + postcss: 8.5.28 + postcss-values-parser: 6.0.2(postcss@8.5.28) + + detective-sass@6.0.2: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + detective-scss@5.0.2: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + detective-stylus@5.0.1: {} + + detective-typescript@14.1.2(typescript@5.9.3): + dependencies: + '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + detective-vue2@2.3.0(typescript@5.9.3): + dependencies: + '@dependents/detective-less': 5.0.3 + '@vue/compiler-sfc': 3.5.42 + detective-es6: 5.0.2 + detective-sass: 6.0.2 + detective-scss: 5.0.2 + detective-stylus: 5.0.1 + detective-typescript: 14.1.2(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + devlop@1.1.0: dependencies: dequal: 2.0.3 didyoumean@1.2.2: {} + diff-sequences@29.6.3: {} + diff@8.0.3: {} diffable-html@4.1.0: @@ -11773,6 +15935,8 @@ snapshots: dlv@1.1.3: {} + dnssd-advertise@1.1.6: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -11828,11 +15992,11 @@ snapshots: electron-to-chromium@1.5.286: {} - embla-carousel-react@8.6.0(react@19.2.4): + embla-carousel-react@8.6.0(react@19.2.3): dependencies: embla-carousel: 8.6.0 embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) - react: 19.2.4 + react: 19.2.3 embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): dependencies: @@ -11850,14 +16014,23 @@ snapshots: empathic@2.0.0: {} + encodeurl@1.0.2: {} + encodeurl@2.0.0: {} + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + entities@1.1.2: {} entities@2.2.0: {} entities@6.0.1: {} + entities@7.0.1: {} + env-paths@2.2.1: {} environment@1.1.0: {} @@ -11866,6 +16039,10 @@ snapshots: dependencies: is-arrayish: 0.2.1 + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 @@ -12090,6 +16267,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + escalade@3.2.0: {} escape-carriage@1.3.1: {} @@ -12104,6 +16310,14 @@ snapshots: escape-string-regexp@5.0.0: {} + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)): dependencies: eslint: 9.39.2(jiti@1.21.7) @@ -12390,6 +16604,8 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 + event-target-shim@5.0.1: {} + eventsource-parser@3.0.6: {} eventsource@3.0.7: @@ -12435,6 +16651,14 @@ snapshots: expect-type@1.3.0: {} + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + expect@30.3.0: dependencies: '@jest/expect-utils': 30.3.0 @@ -12444,6 +16668,117 @@ snapshots: jest-mock: 30.3.0 jest-util: 30.3.0 + expo-asset@57.0.16(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3): + dependencies: + '@expo/image-utils': 0.11.5(typescript@5.9.3) + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + expo-constants: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)) + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + transitivePeerDependencies: + - supports-color + - typescript + + expo-constants@57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)): + dependencies: + '@expo/env': 2.4.3 + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + transitivePeerDependencies: + - supports-color + + expo-doctor@1.20.4: {} + + expo-file-system@57.0.6(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)): + dependencies: + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + expo-font@57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3): + dependencies: + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + fontfaceobserver: 2.3.0 + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + expo-keep-awake@57.0.1(expo@57.0.19)(react@19.2.3): + dependencies: + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + react: 19.2.3 + + expo-modules-autolinking@57.0.12(typescript@5.9.3): + dependencies: + '@expo/require-utils': 57.0.5(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + commander: 7.2.0 + transitivePeerDependencies: + - supports-color + - typescript + + expo-modules-core@57.0.15(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3): + dependencies: + '@expo/expo-modules-macros-plugin': 0.6.1 + expo-modules-jsi: 57.0.7(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)) + invariant: 2.2.4 + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + expo-modules-jsi@57.0.7(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)): + dependencies: + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + expo-server@57.0.3: {} + + expo-status-bar@57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3): + dependencies: + expo: 57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + + expo@57.0.19(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3): + dependencies: + '@babel/runtime': 7.29.2 + '@expo/cli': 57.0.21(@expo/dom-webview@57.0.1)(expo-constants@57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)))(expo-font@57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3))(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@expo/config': 57.0.9(typescript@5.9.3) + '@expo/config-plugins': 57.0.9(typescript@5.9.3) + '@expo/devtools': 57.0.1(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + '@expo/fingerprint': 0.20.12 + '@expo/local-build-cache-provider': 57.0.8(typescript@5.9.3) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + '@expo/metro': 56.0.2 + '@expo/metro-config': 57.0.12(expo@57.0.19)(typescript@5.9.3) + '@ungap/structured-clone': 1.3.0 + babel-preset-expo: 57.0.10(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@57.0.19)(react-refresh@0.14.2) + expo-asset: 57.0.16(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + expo-constants: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)) + expo-file-system: 57.0.6(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3)) + expo-font: 57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + expo-keep-awake: 57.0.1(expo@57.0.19)(react@19.2.3) + expo-modules-autolinking: 57.0.12(typescript@5.9.3) + expo-modules-core: 57.0.15(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + pretty-format: 29.7.0 + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3) + react-refresh: 0.14.2 + whatwg-url-minimum: 0.1.2 + optionalDependencies: + '@expo/dom-webview': 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + react-dom: 19.2.3(react@19.2.3) + react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - expo-router + - expo-widgets + - react-native-worklets + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + + exponential-backoff@3.1.3: {} + express-rate-limit@8.2.2(express@5.2.1): dependencies: express: 5.2.1 @@ -12526,10 +16861,26 @@ snapshots: dependencies: format: 0.2.2 + fb-dotslash@0.5.8: {} + fb-watchman@2.0.2: dependencies: bser: 2.1.1 + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5: + dependencies: + cross-fetch: 3.2.0 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.41 + transitivePeerDependencies: + - encoding + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -12539,6 +16890,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fetch-nodeshim@0.4.10: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -12549,10 +16902,36 @@ snapshots: filesize@10.1.6: {} + filing-cabinet@5.5.1: + dependencies: + app-module-path: 2.2.0 + commander: 12.1.0 + enhanced-resolve: 5.24.5 + module-definition: 6.0.2 + module-lookup-amd: 9.1.3 + resolve: 1.22.12 + resolve-dependency-path: 4.0.1 + sass-lookup: 6.1.2 + stylus-lookup: 6.1.2 + tsconfig-paths: 4.2.0 + typescript: 5.9.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + finalhandler@1.1.2: + 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 + transitivePeerDependencies: + - supports-color + finalhandler@2.1.1: dependencies: debug: 4.4.3 @@ -12610,8 +16989,12 @@ snapshots: flatted@3.4.2: {} + flow-enums-runtime@0.0.6: {} + follow-redirects@1.16.0: {} + fontfaceobserver@2.3.0: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -12644,6 +17027,8 @@ snapshots: fraction.js@5.3.4: {} + fresh@0.5.2: {} + fresh@2.0.0: {} fromentries@1.3.2: {} @@ -12683,6 +17068,11 @@ snapshots: gensync@1.0.0-beta.2: {} + get-amd-module-type@6.0.2: + dependencies: + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + get-caller-file@2.0.5: {} get-east-asian-width@1.4.0: {} @@ -12704,6 +17094,8 @@ snapshots: get-own-enumerable-keys@1.0.0: {} + get-own-enumerable-property-symbols@3.0.2: {} + get-package-type@0.1.0: {} get-proto@1.0.1: @@ -12728,6 +17120,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + getenv@2.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -12895,12 +17289,32 @@ snapshots: headers-polyfill@4.0.3: {} + hermes-compiler@250829098.0.17: {} + hermes-estree@0.25.1: {} + hermes-estree@0.35.0: {} + + hermes-estree@0.36.0: {} + + hermes-estree@0.36.1: {} + hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 + hermes-parser@0.35.0: + dependencies: + hermes-estree: 0.35.0 + + hermes-parser@0.36.0: + dependencies: + hermes-estree: 0.36.0 + + hermes-parser@0.36.1: + dependencies: + hermes-estree: 0.36.1 + highlight.js@10.7.3: {} highlightjs-vue@1.0.0: {} @@ -12911,6 +17325,10 @@ snapshots: hono@4.12.18: {} + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -12956,6 +17374,8 @@ snapshots: human-signals@8.0.1: {} + hyphenate-style-name@1.1.0: {} + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -13001,10 +17421,14 @@ snapshots: inline-style-parser@0.2.7: {} - input-otp@1.4.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + inline-style-prefixer@7.0.1: dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + css-in-js-utils: 3.1.0 + + input-otp@1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) internal-slot@1.1.0: dependencies: @@ -13019,6 +17443,10 @@ snapshots: '@formatjs/fast-memoize': 3.1.6 '@formatjs/icu-messageformat-parser': 3.5.11 + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + ip-address@10.1.1: {} ipaddr.js@1.9.1: {} @@ -13082,6 +17510,8 @@ snapshots: is-decimal@2.0.1: {} + is-docker@2.2.1: {} + is-docker@3.0.0: {} is-extendable@0.1.1: {} @@ -13126,6 +17556,8 @@ snapshots: dependencies: is-docker: 3.0.0 + is-interactive@1.0.0: {} + is-interactive@2.0.0: {} is-map@2.0.3: {} @@ -13141,6 +17573,8 @@ snapshots: is-number@7.0.0: {} + is-obj@1.0.1: {} + is-obj@3.0.0: {} is-plain-obj@4.1.0: {} @@ -13156,6 +17590,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + is-regexp@1.0.0: {} + is-regexp@3.1.0: {} is-set@2.0.3: {} @@ -13185,10 +17621,14 @@ snapshots: is-typedarray@1.0.0: {} + is-unicode-supported@0.1.0: {} + is-unicode-supported@1.3.0: {} is-unicode-supported@2.1.0: {} + is-url-superb@4.0.0: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -13204,6 +17644,10 @@ snapshots: is-windows@1.0.2: {} + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 @@ -13229,6 +17673,16 @@ snapshots: transitivePeerDependencies: - supports-color + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 @@ -13290,12 +17744,44 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + jest-changed-files@30.3.0: dependencies: execa: 5.1.1 jest-util: 30.3.0 p-limit: 3.1.0 + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-circus@30.3.0: dependencies: '@jest/environment': 30.3.0 @@ -13322,6 +17808,25 @@ snapshots: - babel-plugin-macros - supports-color + jest-cli@29.7.0(@types/node@22.19.10): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.19.10) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.19.10) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@30.3.0(@types/node@22.19.10): dependencies: '@jest/core': 30.3.0 @@ -13341,6 +17846,36 @@ snapshots: - supports-color - ts-node + jest-config@29.7.0(@types/node@22.19.10): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.19.10 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@30.3.0(@types/node@22.19.10): dependencies: '@babel/core': 7.29.0 @@ -13372,6 +17907,13 @@ snapshots: - babel-plugin-macros - supports-color + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + jest-diff@30.3.0: dependencies: '@jest/diff-sequences': 30.3.0 @@ -13379,10 +17921,22 @@ snapshots: chalk: 4.1.2 pretty-format: 30.3.0 + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + jest-docblock@30.2.0: dependencies: detect-newline: 3.1.0 + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + jest-each@30.3.0: dependencies: '@jest/get-type': 30.1.0 @@ -13391,6 +17945,15 @@ snapshots: jest-util: 30.3.0 pretty-format: 30.3.0 + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jest-environment-node@30.3.0: dependencies: '@jest/environment': 30.3.0 @@ -13401,6 +17964,24 @@ snapshots: jest-util: 30.3.0 jest-validate: 30.3.0 + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 22.19.10 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + jest-haste-map@30.3.0: dependencies: '@jest/types': 30.3.0 @@ -13423,11 +18004,23 @@ snapshots: uuid: 8.3.2 xml: 1.0.1 + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + jest-leak-detector@30.3.0: dependencies: '@jest/get-type': 30.1.0 pretty-format: 30.3.0 + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + jest-matcher-utils@30.3.0: dependencies: '@jest/get-type': 30.1.0 @@ -13435,6 +18028,18 @@ snapshots: jest-diff: 30.3.0 pretty-format: 30.3.0 + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + jest-message-util@30.3.0: dependencies: '@babel/code-frame': 7.29.0 @@ -13447,12 +18052,22 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + jest-util: 29.7.0 + jest-mock@30.3.0: dependencies: '@jest/types': 30.3.0 '@types/node': 22.19.10 jest-util: 30.3.0 + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): optionalDependencies: jest-resolve: 30.3.0 @@ -13473,8 +18088,17 @@ snapshots: - debug - supports-color + jest-regex-util@29.6.3: {} + jest-regex-util@30.0.1: {} + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + jest-resolve-dependencies@30.3.0: dependencies: jest-regex-util: 30.0.1 @@ -13482,6 +18106,18 @@ snapshots: transitivePeerDependencies: - supports-color + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.11 + resolve.exports: 2.0.3 + slash: 3.0.0 + jest-resolve@30.3.0: dependencies: chalk: 4.1.2 @@ -13493,6 +18129,32 @@ snapshots: slash: 3.0.0 unrs-resolver: 1.11.1 + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + jest-runner@30.3.0: dependencies: '@jest/console': 30.3.0 @@ -13520,6 +18182,33 @@ snapshots: transitivePeerDependencies: - supports-color + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + jest-runtime@30.3.0: dependencies: '@jest/environment': 30.3.0 @@ -13551,6 +18240,31 @@ snapshots: dependencies: diffable-html: 4.1.0 + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + jest-snapshot@30.3.0: dependencies: '@babel/core': 7.29.0 @@ -13577,6 +18291,15 @@ snapshots: transitivePeerDependencies: - supports-color + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.2 + jest-util@30.3.0: dependencies: '@jest/types': 30.3.0 @@ -13586,6 +18309,15 @@ snapshots: graceful-fs: 4.2.11 picomatch: 4.0.4 + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + jest-validate@30.3.0: dependencies: '@jest/get-type': 30.1.0 @@ -13606,6 +18338,17 @@ snapshots: string-length: 6.0.0 strip-ansi: 7.1.2 + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.10 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + jest-watcher@30.3.0: dependencies: '@jest/test-result': 30.3.0 @@ -13617,6 +18360,13 @@ snapshots: jest-util: 30.3.0 string-length: 4.0.2 + jest-worker@29.7.0: + dependencies: + '@types/node': 22.19.10 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + jest-worker@30.3.0: dependencies: '@types/node': 22.19.10 @@ -13625,6 +18375,18 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 + jest@29.7.0(@types/node@22.19.10): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.19.10) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@30.3.0(@types/node@22.19.10): dependencies: '@jest/core': 30.3.0 @@ -13638,6 +18400,8 @@ snapshots: - supports-color - ts-node + jimp-compact@0.16.1: {} + jiti@1.21.7: {} joi@17.13.3: @@ -13665,6 +18429,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsc-safe-url@0.2.4: {} + jsdom@26.1.0: dependencies: cssstyle: 4.6.0 @@ -13685,7 +18451,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.19.0 + ws: 8.21.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -13735,18 +18501,76 @@ snapshots: kleur@4.1.5: {} - language-subtag-registry@0.3.23: {} + lan-network@0.2.1: {} + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lighthouse-logger@1.4.2: + dependencies: + debug: 2.6.9 + marky: 1.3.0 + transitivePeerDependencies: + - supports-color + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 + lightningcss-win32-arm64-msvc@1.33.0: + optional: true - leven@3.1.0: {} + lightningcss-win32-x64-msvc@1.33.0: + optional: true - levn@0.4.1: + lightningcss@1.33.0: dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 lilconfig@3.1.3: {} @@ -13762,12 +18586,25 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.debounce@4.0.8: {} + lodash.flattendeep@4.4.0: {} lodash.merge@4.6.2: {} + lodash.throttle@4.1.1: {} + lodash@4.18.1: {} + log-symbols@2.2.0: + dependencies: + chalk: 2.4.2 + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + log-symbols@6.0.0: dependencies: chalk: 5.6.2 @@ -13796,12 +18633,35 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.468.0(react@19.2.3): + dependencies: + react: 19.2.3 + lucide-react@0.468.0(react@19.2.4): dependencies: react: 19.2.4 lz-string@1.5.0: {} + madge@8.0.0(typescript@5.9.3): + dependencies: + chalk: 4.1.2 + commander: 7.2.0 + commondir: 1.0.1 + debug: 4.4.3 + dependency-tree: 11.5.0 + ora: 5.4.1 + pluralize: 8.0.0 + pretty-ms: 7.0.1 + rc: 1.2.8 + stream-to-array: 2.3.0 + ts-graphviz: 2.1.6 + walkdir: 0.4.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -13828,6 +18688,8 @@ snapshots: markdown-table@3.0.4: {} + marky@1.3.0: {} + math-intrinsics@1.1.0: {} mdast-util-find-and-replace@3.0.2: @@ -13995,12 +18857,362 @@ snapshots: media-typer@1.1.0: {} + memoize-one@5.2.1: {} + + memoize-one@6.0.0: {} + merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} merge2@1.4.1: {} + metro-babel-transformer@0.84.5: + dependencies: + '@babel/core': 7.29.0 + flow-enums-runtime: 0.0.6 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.5 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-babel-transformer@0.84.6: + dependencies: + '@babel/core': 7.29.0 + flow-enums-runtime: 0.0.6 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-cache-key@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-cache-key@0.84.6: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-cache@0.84.5: + dependencies: + exponential-backoff: 3.1.3 + flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6 + metro-core: 0.84.5 + transitivePeerDependencies: + - supports-color + + metro-cache@0.84.6: + dependencies: + exponential-backoff: 3.1.3 + flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6 + metro-core: 0.84.6 + transitivePeerDependencies: + - supports-color + + metro-config@0.84.5: + dependencies: + connect: 3.7.0 + flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.84.5 + metro-cache: 0.84.5 + metro-core: 0.84.5 + metro-runtime: 0.84.5 + yaml: 2.9.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-config@0.84.6: + dependencies: + connect: 3.7.0 + flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.84.6 + metro-cache: 0.84.6 + metro-core: 0.84.6 + metro-runtime: 0.84.6 + yaml: 2.9.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-core@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + lodash.throttle: 4.1.1 + metro-resolver: 0.84.5 + + metro-core@0.84.6: + dependencies: + flow-enums-runtime: 0.0.6 + lodash.throttle: 4.1.1 + metro-resolver: 0.84.6 + + metro-file-map@0.84.5: + dependencies: + debug: 4.4.3 + fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + nullthrows: 1.1.1 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + metro-file-map@0.84.6: + dependencies: + debug: 4.4.3 + fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + nullthrows: 1.1.1 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + metro-minify-terser@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + terser: 5.51.2 + + metro-minify-terser@0.84.6: + dependencies: + flow-enums-runtime: 0.0.6 + terser: 5.51.2 + + metro-resolver@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-resolver@0.84.6: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-runtime@0.84.5: + dependencies: + '@babel/runtime': 7.29.2 + flow-enums-runtime: 0.0.6 + + metro-runtime@0.84.6: + dependencies: + '@babel/runtime': 7.29.2 + flow-enums-runtime: 0.0.6 + + metro-source-map@0.84.5: + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.84.5 + nullthrows: 1.1.1 + ob1: 0.84.5 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-source-map@0.84.6: + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.84.6 + nullthrows: 1.1.1 + ob1: 0.84.6 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-source-map: 0.84.5 + nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.84.6: + dependencies: + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-source-map: 0.84.6 + nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.84.5: + dependencies: + '@babel/core': 7.29.0 + '@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 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.84.6: + dependencies: + '@babel/core': 7.29.0 + '@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 + transitivePeerDependencies: + - supports-color + + metro-transform-worker@0.84.5: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + metro: 0.84.5 + metro-babel-transformer: 0.84.5 + metro-cache: 0.84.5 + metro-cache-key: 0.84.5 + metro-minify-terser: 0.84.5 + metro-source-map: 0.84.5 + metro-transform-plugins: 0.84.5 + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-transform-worker@0.84.6: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + metro: 0.84.6 + metro-babel-transformer: 0.84.6 + metro-cache: 0.84.6 + metro-cache-key: 0.84.6 + metro-minify-terser: 0.84.6 + metro-source-map: 0.84.6 + metro-transform-plugins: 0.84.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro@0.84.5: + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@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 + ci-info: 2.0.0 + connect: 3.7.0 + debug: 4.4.3 + error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + hermes-parser: 0.35.0 + invariant: 2.2.4 + jest-worker: 29.7.0 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.84.5 + metro-cache: 0.84.5 + metro-cache-key: 0.84.5 + metro-config: 0.84.5 + metro-core: 0.84.5 + metro-file-map: 0.84.5 + metro-resolver: 0.84.5 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5 + metro-symbolicate: 0.84.5 + metro-transform-plugins: 0.84.5 + metro-transform-worker: 0.84.5 + mime-types: 3.0.2 + nullthrows: 1.1.1 + serialize-error: 2.1.0 + source-map: 0.5.7 + throat: 5.0.0 + ws: 7.5.13 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro@0.84.6: + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@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 + ci-info: 2.0.0 + connect: 3.7.0 + debug: 4.4.3 + error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + hermes-parser: 0.35.0 + invariant: 2.2.4 + jest-worker: 29.7.0 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.84.6 + metro-cache: 0.84.6 + metro-cache-key: 0.84.6 + metro-config: 0.84.6 + metro-core: 0.84.6 + metro-file-map: 0.84.6 + metro-resolver: 0.84.6 + metro-runtime: 0.84.6 + metro-source-map: 0.84.6 + metro-symbolicate: 0.84.6 + metro-transform-plugins: 0.84.6 + metro-transform-worker: 0.84.6 + mime-types: 3.0.2 + nullthrows: 1.1.1 + serialize-error: 2.1.0 + source-map: 0.5.7 + throat: 5.0.0 + ws: 7.5.13 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -14282,6 +19494,10 @@ snapshots: dependencies: mime-db: 1.54.0 + mime@1.6.0: {} + + mimic-fn@1.2.0: {} + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -14309,6 +19525,19 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.3 + module-definition@6.0.2: + dependencies: + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + + module-lookup-amd@9.1.3: + dependencies: + commander: 12.1.0 + requirejs: 2.3.8 + requirejs-config-file: 4.0.0 + + ms@2.0.0: {} + ms@2.1.3: {} msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3): @@ -14336,6 +19565,8 @@ snapshots: transitivePeerDependencies: - '@types/node' + multitars@1.0.2: {} + mute-stream@2.0.0: {} mz@2.7.0: @@ -14344,7 +19575,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.12: {} + nanoid@3.3.18: {} napi-postinstall@0.3.4: {} @@ -14352,20 +19583,24 @@ snapshots: natural-orderby@5.0.0: {} + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + negotiator@1.0.0: {} neo-async@2.6.2: {} next-intl-swc-plugin-extractor@4.13.0: {} - next-intl@4.13.0(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + next-intl@4.13.0(next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: '@formatjs/intl-localematcher': 0.8.10 '@parcel/watcher': 2.5.6 '@swc/core': 1.15.18 icu-minify: 4.13.0 negotiator: 1.0.0 - next: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-intl-swc-plugin-extractor: 4.13.0 po-parser: 2.1.1 react: 19.2.4 @@ -14375,20 +19610,46 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - next-themes@0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next-themes@0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) next-tick@1.1.0: {} - next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@next/env': 16.2.6 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.9.19 + caniuse-lite: 1.0.30001769 + postcss: 8.5.28 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3) + optionalDependencies: + '@next/swc-darwin-arm64': 16.2.6 + '@next/swc-darwin-x64': 16.2.6 + '@next/swc-linux-arm64-gnu': 16.2.6 + '@next/swc-linux-arm64-musl': 16.2.6 + '@next/swc-linux-x64-gnu': 16.2.6 + '@next/swc-linux-x64-musl': 16.2.6 + '@next/swc-win32-arm64-msvc': 16.2.6 + '@next/swc-win32-x64-msvc': 16.2.6 + '@playwright/test': 1.60.0 + babel-plugin-react-compiler: 1.0.0 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.2.6 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.9.19 caniuse-lite: 1.0.30001769 - postcss: 8.5.10 + postcss: 8.5.28 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) @@ -14402,6 +19663,7 @@ snapshots: '@next/swc-win32-arm64-msvc': 16.2.6 '@next/swc-win32-x64-msvc': 16.2.6 '@playwright/test': 1.60.0 + babel-plugin-react-compiler: 1.0.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -14413,12 +19675,18 @@ snapshots: node-domexception@1.0.0: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-forge@1.4.0: {} + node-int64@0.4.0: {} node-preload@0.2.1: @@ -14427,8 +19695,19 @@ snapshots: node-releases@2.0.27: {} + node-source-walk@7.0.2: + dependencies: + '@babel/parser': 7.29.8 + normalize-path@3.0.0: {} + npm-package-arg@11.0.3: + dependencies: + hosted-git-info: 7.0.2 + proc-log: 4.2.0 + semver: 7.7.4 + validate-npm-package-name: 5.0.1 + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -14438,6 +19717,8 @@ snapshots: path-key: 4.0.0 unicorn-magic: 0.3.0 + nullthrows@1.1.1: {} + nwsapi@2.2.23: {} nyc@15.1.0: @@ -14472,6 +19753,14 @@ snapshots: transitivePeerDependencies: - supports-color + ob1@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + + ob1@0.84.6: + dependencies: + flow-enums-runtime: 0.0.6 + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -14514,14 +19803,24 @@ snapshots: obug@2.1.1: {} + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 + on-headers@1.1.0: {} + once@1.4.0: dependencies: wrappy: 1.0.2 + onetime@2.0.1: + dependencies: + mimic-fn: 1.2.0 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -14546,6 +19845,11 @@ snapshots: powershell-utils: 0.1.0 wsl-utils: 0.3.1 + open@7.4.2: + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -14555,6 +19859,27 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@3.4.0: + dependencies: + chalk: 2.4.2 + cli-cursor: 2.1.0 + cli-spinners: 2.9.2 + log-symbols: 2.2.0 + strip-ansi: 5.2.0 + wcwidth: 1.0.1 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + ora@8.2.0: dependencies: chalk: 5.6.2 @@ -14641,10 +19966,16 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@2.1.0: {} + parse-ms@4.0.0: {} parse-passwd@1.0.0: {} + parse-png@2.1.0: + dependencies: + pngjs: 3.4.0 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -14723,37 +20054,46 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + plist@3.1.1: + dependencies: + '@xmldom/xmldom': 0.9.12 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + pluralize@8.0.0: {} + pngjs@3.4.0: {} + pngjs@5.0.0: {} po-parser@2.1.1: {} possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.10): + postcss-import@15.1.0(postcss@8.5.28): dependencies: - postcss: 8.5.10 + postcss: 8.5.28 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.11 - postcss-js@4.1.0(postcss@8.5.10): + postcss-js@4.1.0(postcss@8.5.28): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.10 + postcss: 8.5.28 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(yaml@2.9.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 - postcss: 8.5.10 + postcss: 8.5.28 tsx: 4.21.0 + yaml: 2.9.0 - postcss-nested@6.2.0(postcss@8.5.10): + postcss-nested@6.2.0(postcss@8.5.28): dependencies: - postcss: 8.5.10 + postcss: 8.5.28 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.0.10: @@ -14773,14 +20113,41 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.10: + postcss-values-parser@6.0.2(postcss@8.5.28): + dependencies: + color-name: 1.1.4 + is-url-superb: 4.0.0 + postcss: 8.5.28 + quote-unquote: 1.0.0 + + postcss@8.5.28: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 powershell-utils@0.1.0: {} + precinct@12.3.2: + dependencies: + '@dependents/detective-less': 5.0.3 + commander: 12.1.0 + detective-amd: 6.1.0 + detective-cjs: 6.1.1 + detective-es6: 5.0.2 + detective-postcss: 8.0.4(postcss@8.5.28) + detective-sass: 6.0.2 + detective-scss: 5.0.2 + detective-stylus: 5.0.1 + detective-typescript: 14.1.2(typescript@5.9.3) + detective-vue2: 2.3.0(typescript@5.9.3) + module-definition: 6.0.2 + node-source-walk: 7.0.2 + postcss: 8.5.28 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.1: @@ -14795,22 +20162,44 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + pretty-format@30.3.0: dependencies: '@jest/schemas': 30.0.5 ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-ms@7.0.1: + dependencies: + parse-ms: 2.1.0 + pretty-ms@9.3.0: dependencies: parse-ms: 4.0.0 prismjs@1.30.0: {} + proc-log@4.2.0: {} + process-on-spawn@1.1.0: dependencies: fromentries: 1.3.2 + progress@2.0.3: {} + + promise@7.3.1: + dependencies: + asap: 2.0.6 + + promise@8.3.0: + dependencies: + asap: 2.0.6 + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -14833,6 +20222,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + pure-rand@7.0.1: {} qrcode@1.5.4: @@ -14847,6 +20238,8 @@ snapshots: queue-microtask@1.2.3: {} + quote-unquote@1.0.0: {} + range-parser@1.2.1: {} raw-body@3.0.2: @@ -14856,12 +20249,27 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 - react-day-picker@9.13.2(react@19.2.4): + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-day-picker@9.13.2(react@19.2.3): dependencies: '@date-fns/tz': 1.4.1 date-fns: 4.1.0 date-fns-jalali: 4.1.0-0 - react: 19.2.4 + react: 19.2.3 + + react-devtools-core@6.1.5: + dependencies: + shell-quote: 1.10.0 + ws: 7.5.13 + transitivePeerDependencies: + - bufferutil + - utf-8-validate react-devtools-inline@4.4.0: dependencies: @@ -14886,14 +20294,19 @@ snapshots: transitivePeerDependencies: - supports-color + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 scheduler: 0.27.0 - react-hook-form@7.73.1(react@19.2.4): + react-hook-form@7.73.1(react@19.2.3): dependencies: - react: 19.2.4 + react: 19.2.3 react-is@16.13.1: {} @@ -14901,6 +20314,26 @@ snapshots: react-is@18.3.1: {} + react-is@19.2.8: {} + + react-markdown@10.1.0(@types/react@19.2.13)(react@19.2.3): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.13 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.3 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-markdown@10.1.0(@types/react@19.2.13)(react@19.2.4): dependencies: '@types/hast': 3.0.4 @@ -14919,50 +20352,121 @@ snapshots: transitivePeerDependencies: - supports-color + react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@babel/runtime': 7.29.2 + '@react-native/normalize-colors': 0.74.89 + fbjs: 3.0.5 + inline-style-prefixer: 7.0.1 + memoize-one: 6.0.0 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styleq: 0.1.3 + transitivePeerDependencies: + - encoding + + react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3): + dependencies: + '@react-native/assets-registry': 0.86.3 + '@react-native/codegen': 0.86.3(@babel/core@7.29.0) + '@react-native/community-cli-plugin': 0.86.3 + '@react-native/gradle-plugin': 0.86.3 + '@react-native/js-polyfills': 0.86.3 + '@react-native/normalize-colors': 0.86.3 + '@react-native/virtualized-lists': 0.86.3(@types/react@19.2.13)(react-native@0.86.3(@babel/core@7.29.0)(@react-native/jest-preset@0.86.3(@babel/core@7.29.0)(react@19.2.3))(@types/react@19.2.13)(react@19.2.3))(react@19.2.3) + abort-controller: 3.0.0 + anser: 1.4.10 + ansi-regex: 5.0.1 + babel-plugin-syntax-hermes-parser: 0.36.0 + base64-js: 1.5.1 + commander: 12.1.0 + flow-enums-runtime: 0.0.6 + hermes-compiler: 250829098.0.17 + invariant: 2.2.4 + memoize-one: 5.2.1 + metro-runtime: 0.84.6 + metro-source-map: 0.84.6 + nullthrows: 1.1.1 + pretty-format: 29.7.0 + promise: 8.3.0 + react: 19.2.3 + react-devtools-core: 6.1.5 + react-refresh: 0.14.2 + regenerator-runtime: 0.13.11 + scheduler: 0.27.0 + semver: 7.7.4 + stacktrace-parser: 0.1.11 + tinyglobby: 0.2.15 + whatwg-fetch: 3.6.20 + ws: 7.5.13 + yargs: 17.7.2 + optionalDependencies: + '@react-native/jest-preset': 0.86.3(@babel/core@7.29.0)(react@19.2.3) + '@types/react': 19.2.13 + transitivePeerDependencies: + - '@babel/core' + - '@react-native-community/cli' + - '@react-native/metro-config' + - bufferutil + - supports-color + - utf-8-validate + + react-refresh@0.14.2: {} + react-refresh@0.17.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.13)(react@19.2.4): + react-remove-scroll-bar@2.3.8(@types/react@19.2.13)(react@19.2.3): dependencies: - react: 19.2.4 - react-style-singleton: 2.2.3(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-style-singleton: 2.2.3(@types/react@19.2.13)(react@19.2.3) tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.13 - react-remove-scroll@2.7.2(@types/react@19.2.13)(react@19.2.4): + react-remove-scroll@2.7.2(@types/react@19.2.13)(react@19.2.3): dependencies: - react: 19.2.4 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.13)(react@19.2.4) - react-style-singleton: 2.2.3(@types/react@19.2.13)(react@19.2.4) + react: 19.2.3 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.13)(react@19.2.3) + react-style-singleton: 2.2.3(@types/react@19.2.13)(react@19.2.3) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.13)(react@19.2.4) - use-sidecar: 1.1.3(@types/react@19.2.13)(react@19.2.4) + use-callback-ref: 1.3.3(@types/react@19.2.13)(react@19.2.3) + use-sidecar: 1.1.3(@types/react@19.2.13)(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 - react-resizable-panels@4.6.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + react-resizable-panels@4.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-style-singleton@2.2.3(@types/react@19.2.13)(react@19.2.4): + react-style-singleton@2.2.3(@types/react@19.2.13)(react@19.2.3): dependencies: get-nonce: 1.0.1 - react: 19.2.4 + react: 19.2.3 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.13 - react-syntax-highlighter@16.1.1(react@19.2.4): + react-syntax-highlighter@16.1.1(react@19.2.3): dependencies: '@babel/runtime': 7.28.6 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 prismjs: 1.30.0 - react: 19.2.4 + react: 19.2.3 refractor: 5.0.0 + react-test-renderer@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + react-is: 19.2.8 + scheduler: 0.27.0 + + react@19.2.3: {} + react@19.2.4: {} read-cache@1.0.0: @@ -15041,6 +20545,14 @@ snapshots: hastscript: 9.0.1 parse-entities: 4.0.2 + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.13.11: {} + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.4: @@ -15052,6 +20564,17 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + regjsparser@0.13.0: dependencies: jsesc: 3.1.0 @@ -15117,10 +20640,19 @@ snapshots: requireindex@1.1.0: {} + requirejs-config-file@4.0.0: + dependencies: + esprima: 4.0.1 + stringify-object: 3.3.0 + + requirejs@2.3.8: {} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 + resolve-dependency-path@4.0.1: {} + resolve-dir@0.1.1: dependencies: expand-tilde: 1.2.2 @@ -15132,18 +20664,39 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve-workspace-root@2.0.1: {} + + resolve.exports@2.0.3: {} + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.5: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@2.0.0: + dependencies: + onetime: 2.0.1 + signal-exit: 3.0.7 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -15188,6 +20741,38 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 + rollup@4.63.1: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.63.1 + '@rollup/rollup-android-arm64': 4.63.1 + '@rollup/rollup-darwin-arm64': 4.63.1 + '@rollup/rollup-darwin-x64': 4.63.1 + '@rollup/rollup-freebsd-arm64': 4.63.1 + '@rollup/rollup-freebsd-x64': 4.63.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.1 + '@rollup/rollup-linux-arm-musleabihf': 4.63.1 + '@rollup/rollup-linux-arm64-gnu': 4.63.1 + '@rollup/rollup-linux-arm64-musl': 4.63.1 + '@rollup/rollup-linux-loong64-gnu': 4.63.1 + '@rollup/rollup-linux-loong64-musl': 4.63.1 + '@rollup/rollup-linux-ppc64-gnu': 4.63.1 + '@rollup/rollup-linux-ppc64-musl': 4.63.1 + '@rollup/rollup-linux-riscv64-gnu': 4.63.1 + '@rollup/rollup-linux-riscv64-musl': 4.63.1 + '@rollup/rollup-linux-s390x-gnu': 4.63.1 + '@rollup/rollup-linux-x64-gnu': 4.63.1 + '@rollup/rollup-linux-x64-musl': 4.63.1 + '@rollup/rollup-openbsd-x64': 4.63.1 + '@rollup/rollup-openharmony-arm64': 4.63.1 + '@rollup/rollup-win32-arm64-msvc': 4.63.1 + '@rollup/rollup-win32-ia32-msvc': 4.63.1 + '@rollup/rollup-win32-x64-gnu': 4.63.1 + '@rollup/rollup-win32-x64-msvc': 4.63.1 + fsevents: 2.3.3 + router@2.2.0: dependencies: debug: 4.4.3 @@ -15233,6 +20818,15 @@ snapshots: safer-buffer@2.1.2: {} + sandbox-cli-detector@0.2.0: {} + + sass-lookup@6.1.2: + dependencies: + commander: 12.1.0 + enhanced-resolve: 5.24.5 + + sax@1.6.1: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -15248,6 +20842,24 @@ snapshots: semver@7.7.4: {} + send@0.19.2: + 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 + transitivePeerDependencies: + - supports-color + send@1.2.1: dependencies: debug: 4.4.3 @@ -15264,6 +20876,17 @@ snapshots: transitivePeerDependencies: - supports-color + serialize-error@2.1.0: {} + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -15297,6 +20920,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} shadcn@4.2.0-canary.0(@types/node@22.19.10)(typescript@5.9.3): @@ -15324,7 +20949,7 @@ snapshots: node-fetch: 3.3.2 open: 11.0.0 ora: 8.2.0 - postcss: 8.5.10 + postcss: 8.5.28 postcss-selector-parser: 7.1.1 prompts: 2.4.2 recast: 0.23.11 @@ -15380,6 +21005,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.10.0: {} + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -15414,16 +21041,24 @@ snapshots: signal-exit@4.1.0: {} + simple-plist@1.3.1: + dependencies: + bplist-creator: 0.1.0 + bplist-parser: 0.3.1 + plist: 3.1.1 + sisteransi@1.0.5: {} slash@3.0.0: {} slash@5.1.0: {} - sonner@1.7.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + slugify@1.6.9: {} + + sonner@1.7.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) source-map-js@1.2.1: {} @@ -15432,6 +21067,13 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + source-map@0.6.1: {} source-map@0.7.6: {} @@ -15466,6 +21108,12 @@ snapshots: stackback@0.0.2: {} + stackframe@1.3.4: {} + + stacktrace-parser@0.1.11: + dependencies: + type-fest: 0.7.1 + static-browser-server@1.0.3: dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -15473,6 +21121,8 @@ snapshots: mime-db: 1.54.0 outvariant: 1.4.3 + statuses@1.5.0: {} + statuses@2.0.2: {} std-env@4.1.0: {} @@ -15484,10 +21134,10 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + storybook@10.2.17(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@storybook/global': 5.0.0 - '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@testing-library/jest-dom': 6.9.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/expect': 3.2.4 @@ -15496,8 +21146,8 @@ snapshots: open: 10.2.0 recast: 0.23.11 semver: 7.7.4 - use-sync-external-store: 1.6.0(react@19.2.4) - ws: 8.19.0 + use-sync-external-store: 1.6.0(react@19.2.3) + ws: 8.21.3 optionalDependencies: prettier: 3.8.1 transitivePeerDependencies: @@ -15507,6 +21157,12 @@ snapshots: - react-dom - utf-8-validate + stream-buffers@2.2.0: {} + + stream-to-array@2.3.0: + dependencies: + any-promise: 1.3.0 + strict-event-emitter@0.4.6: {} strict-event-emitter@0.5.1: {} @@ -15597,12 +21253,22 @@ snapshots: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + stringify-object@5.0.0: dependencies: get-own-enumerable-keys: 1.0.0 is-obj: 3.0.0 is-regexp: 3.1.0 + strip-ansi@5.2.0: + dependencies: + ansi-regex: 4.1.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -15627,8 +21293,12 @@ snapshots: strip-indent@4.1.1: {} + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} + structured-headers@0.4.1: {} + style-mod@4.1.3: {} style-to-js@1.1.21: @@ -15639,6 +21309,13 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.3): + dependencies: + client-only: 0.0.1 + react: 19.2.3 + optionalDependencies: + '@babel/core': 7.29.0 + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.4): dependencies: client-only: 0.0.1 @@ -15646,6 +21323,12 @@ snapshots: optionalDependencies: '@babel/core': 7.29.0 + styleq@0.1.3: {} + + stylus-lookup@6.1.2: + dependencies: + commander: 12.1.0 + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -15668,6 +21351,11 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-hyperlinks@2.3.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + supports-preserve-symlinks-flag@1.0.0: {} symbol-tree@3.2.4: {} @@ -15682,11 +21370,11 @@ snapshots: tailwind-merge@3.4.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.19(tsx@4.21.0)): + tailwindcss-animate@1.0.7(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0)): dependencies: - tailwindcss: 3.4.19(tsx@4.21.0) + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.9.0) - tailwindcss@3.4.19(tsx@4.21.0): + tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -15702,11 +21390,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.10 - postcss-import: 15.1.0(postcss@8.5.10) - postcss-js: 4.1.0(postcss@8.5.10) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0) - postcss-nested: 6.2.0(postcss@8.5.10) + postcss: 8.5.28 + postcss-import: 15.1.0(postcss@8.5.28) + postcss-js: 4.1.0(postcss@8.5.28) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(yaml@2.9.0) + postcss-nested: 6.2.0(postcss@8.5.28) postcss-selector-parser: 6.1.2 resolve: 1.22.11 sucrase: 3.35.1 @@ -15714,6 +21402,20 @@ snapshots: - tsx - yaml + tapable@2.3.3: {} + + terminal-link@2.1.1: + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + + terser@5.51.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -15728,6 +21430,8 @@ snapshots: dependencies: any-promise: 1.3.0 + throat@5.0.0: {} + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} @@ -15741,6 +21445,11 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + tinyrainbow@2.0.0: {} tinyrainbow@3.1.0: {} @@ -15779,6 +21488,8 @@ snapshots: too-wordy@0.3.6: {} + toqr@0.1.1: {} + tough-cookie@5.1.2: dependencies: tldts: 6.1.86 @@ -15787,6 +21498,8 @@ snapshots: dependencies: tldts: 7.0.23 + tr46@0.0.3: {} + tr46@5.1.1: dependencies: punycode: 2.3.1 @@ -15801,6 +21514,10 @@ snapshots: dependencies: typescript: 5.9.3 + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-declaration-location@1.0.7(typescript@5.9.3): dependencies: picomatch: 4.0.4 @@ -15808,6 +21525,13 @@ snapshots: ts-dedent@2.2.0: {} + ts-graphviz@2.1.6: + dependencies: + '@ts-graphviz/adapter': 2.0.6 + '@ts-graphviz/ast': 2.0.7 + '@ts-graphviz/common': 2.1.5 + '@ts-graphviz/core': 2.0.7 + ts-interface-checker@0.1.13: {} ts-morph@26.0.0: @@ -15823,7 +21547,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(@swc/core@1.15.18)(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0)(typescript@5.9.3): + tsup@8.5.1(@swc/core@1.15.18)(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): dependencies: bundle-require: 5.1.0(esbuild@0.27.3) cac: 6.7.14 @@ -15834,7 +21558,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.10)(tsx@4.21.0) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.28)(tsx@4.21.0)(yaml@2.9.0) resolve-from: 5.0.0 rollup: 4.59.0 source-map: 0.7.6 @@ -15844,7 +21568,7 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: '@swc/core': 1.15.18 - postcss: 8.5.10 + postcss: 8.5.28 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -15894,6 +21618,8 @@ snapshots: type-fest@0.21.3: {} + type-fest@0.7.1: {} + type-fest@0.8.1: {} type-fest@5.4.4: @@ -15958,6 +21684,8 @@ snapshots: typescript@5.9.3: {} + ua-parser-js@1.0.41: {} + ufo@1.6.3: {} uglify-js@3.19.3: @@ -15972,6 +21700,17 @@ snapshots: undici-types@6.21.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + unicorn-magic@0.3.0: {} unified@11.0.5: @@ -16060,9 +21799,9 @@ snapshots: uri-template-matcher@1.1.2: {} - use-callback-ref@1.3.3(@types/react@19.2.13)(react@19.2.4): + use-callback-ref@1.3.3(@types/react@19.2.13)(react@19.2.3): dependencies: - react: 19.2.4 + react: 19.2.3 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.13 @@ -16075,20 +21814,24 @@ snapshots: intl-messageformat: 11.2.8 react: 19.2.4 - use-sidecar@1.1.3(@types/react@19.2.13)(react@19.2.4): + use-sidecar@1.1.3(@types/react@19.2.13)(react@19.2.3): dependencies: detect-node-es: 1.1.0 - react: 19.2.4 + react: 19.2.3 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.13 - use-sync-external-store@1.6.0(react@19.2.4): + use-sync-external-store@1.6.0(react@19.2.3): dependencies: - react: 19.2.4 + react: 19.2.3 util-deprecate@1.0.2: {} + utils-merge@1.0.1: {} + + uuid@7.0.3: {} + uuid@8.3.2: {} v8-to-istanbul@9.3.0: @@ -16101,15 +21844,17 @@ snapshots: optionalDependencies: typescript: 5.9.3 + validate-npm-package-name@5.0.1: {} + validate-npm-package-name@7.0.2: {} vary@1.1.2: {} - vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -16124,38 +21869,44 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@6.4.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0): + vite@6.4.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.10 + postcss: 8.5.28 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.10 fsevents: 2.3.3 jiti: 1.21.7 + lightningcss: 1.33.0 + terser: 5.51.2 tsx: 4.21.0 + yaml: 2.9.0 - vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0): + vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.10 - rollup: 4.59.0 - tinyglobby: 0.2.15 + postcss: 8.5.28 + rollup: 4.63.1 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 22.19.10 fsevents: 2.3.3 jiti: 1.21.7 + lightningcss: 1.33.0 + terser: 5.51.2 tsx: 4.21.0 + yaml: 2.9.0 - vitest@4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)): + vitest@4.1.9(@types/node@22.19.10)(@vitest/coverage-v8@4.1.9)(jsdom@26.1.0)(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0)) + '@vitest/mocker': 4.1.9(msw@2.12.9(@types/node@22.19.10)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.9 '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 @@ -16172,7 +21923,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(tsx@4.21.0) + vite: 7.3.2(@types/node@22.19.10)(jiti@1.21.7)(lightningcss@1.33.0)(terser@5.51.2)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.10 @@ -16181,6 +21932,8 @@ snapshots: transitivePeerDependencies: - msw + vlq@1.0.1: {} + w3c-keyname@2.2.8: {} w3c-xmlserializer@5.0.0: @@ -16205,14 +21958,22 @@ snapshots: transitivePeerDependencies: - supports-color + walkdir@0.4.1: {} + walker@1.0.8: dependencies: makeerror: 1.0.12 + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + weasel-words@0.1.1: {} web-streams-polyfill@3.3.3: {} + webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} webpack-virtual-modules@0.6.2: {} @@ -16221,13 +21982,22 @@ snapshots: dependencies: iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@4.0.0: {} + whatwg-url-minimum@0.1.2: {} + whatwg-url@14.2.0: dependencies: tr46: 5.1.1 webidl-conversions: 7.0.0 + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -16319,6 +22089,11 @@ snapshots: signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 @@ -16334,7 +22109,9 @@ snapshots: too-wordy: 0.3.6 weasel-words: 0.1.1 - ws@8.19.0: {} + ws@7.5.13: {} + + ws@8.21.3: {} wsl-utils@0.1.0: dependencies: @@ -16345,10 +22122,24 @@ snapshots: is-wsl: 3.1.1 powershell-utils: 0.1.0 + xcode@3.0.1: + dependencies: + simple-plist: 1.3.1 + uuid: 7.0.3 + xml-name-validator@5.0.0: {} + xml2js@0.6.0: + dependencies: + sax: 1.6.1 + xmlbuilder: 11.0.1 + xml@1.0.1: {} + xmlbuilder@11.0.1: {} + + xmlbuilder@15.1.1: {} + xmlchars@2.2.0: {} y18n@4.0.3: {} @@ -16357,6 +22148,8 @@ snapshots: yallist@3.1.1: {} + yaml@2.9.0: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 @@ -16406,11 +22199,11 @@ snapshots: zod@4.3.6: {} - zustand@4.5.7(@types/react@19.2.13)(react@19.2.4): + zustand@4.5.7(@types/react@19.2.13)(react@19.2.3): dependencies: - use-sync-external-store: 1.6.0(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: '@types/react': 19.2.13 - react: 19.2.4 + react: 19.2.3 zwitch@2.0.4: {} From 56e28a55fc855ca6d3f5c02935a9255a5286d286 Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:29:39 +0200 Subject: [PATCH 02/18] pi-agent: Native interactive content --- .../src/components/accordion/accordion.tsx | 247 +++++++++ .../animated-list/animated-list.tsx | 106 ++++ .../animated-testimonials.tsx | 250 +++++++++ .../src/components/carousel/carousel.tsx | 233 +++++++++ .../components/collapsible/collapsible.tsx | 219 ++++++++ .../expandable-cards/expandable-cards.tsx | 159 ++++++ packages/ui-native/src/components/faq/faq.tsx | 177 +++++++ .../floating-toolbar/floating-toolbar.tsx | 149 ++++++ .../interactive-data-content-native.test.tsx | 319 ++++++++++++ .../interactive-timeline.tsx | 419 +++++++++++++++ .../scroll-progress/scroll-progress.tsx | 82 +++ .../src/components/slideshow/slideshow.tsx | 475 ++++++++++++++++++ .../src/components/tree-view/tree-view.tsx | 293 +++++++++++ 13 files changed, 3128 insertions(+) create mode 100644 packages/ui-native/src/components/accordion/accordion.tsx create mode 100644 packages/ui-native/src/components/animated-list/animated-list.tsx create mode 100644 packages/ui-native/src/components/animated-testimonials/animated-testimonials.tsx create mode 100644 packages/ui-native/src/components/carousel/carousel.tsx create mode 100644 packages/ui-native/src/components/collapsible/collapsible.tsx create mode 100644 packages/ui-native/src/components/expandable-cards/expandable-cards.tsx create mode 100644 packages/ui-native/src/components/faq/faq.tsx create mode 100644 packages/ui-native/src/components/floating-toolbar/floating-toolbar.tsx create mode 100644 packages/ui-native/src/components/interactive-data-content-native.test.tsx create mode 100644 packages/ui-native/src/components/interactive-timeline/interactive-timeline.tsx create mode 100644 packages/ui-native/src/components/scroll-progress/scroll-progress.tsx create mode 100644 packages/ui-native/src/components/slideshow/slideshow.tsx create mode 100644 packages/ui-native/src/components/tree-view/tree-view.tsx diff --git a/packages/ui-native/src/components/accordion/accordion.tsx b/packages/ui-native/src/components/accordion/accordion.tsx new file mode 100644 index 00000000..1272969d --- /dev/null +++ b/packages/ui-native/src/components/accordion/accordion.tsx @@ -0,0 +1,247 @@ +"use client"; + +import { + createContext, + type ReactNode, + type Ref, + use, + useCallback, + useMemo, +} from "react"; + +import { StyleSheet, Text, View, type ViewProps } from "react-native"; + +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; +import { + Collapsible, + CollapsibleContent, + type CollapsibleContentProps, + CollapsibleTrigger, + type CollapsibleTriggerProps, +} from "../collapsible/collapsible"; + +/** Props for controlled or uncontrolled native accordion state. */ +export type AccordionProps = Omit & { + readonly children: ReactNode; + readonly defaultOpenIds?: readonly string[]; + readonly onOpenIdsChange?: (ids: readonly string[]) => void; + readonly openIds?: readonly string[]; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly type?: "multiple" | "single"; +}; + +/** Props for one caller-identified accordion item. */ +export type AccordionItemProps = Omit & { + readonly children: ReactNode; + readonly id: string; + readonly ref?: Ref; +}; + +/** Props for an accordion item's localized disclosure control. */ +export type AccordionTriggerProps = Omit< + CollapsibleTriggerProps, + "children" | "label" +> & { + readonly icon?: ReactNode; + readonly label: string; +}; + +/** Props for an accordion item's revealed content. */ +export type AccordionContentProps = CollapsibleContentProps; + +type AccordionContextValue = { + readonly isOpen: (id: string) => boolean; + readonly reducedMotionService?: ReducedMotionService; + readonly toggle: (id: string) => void; +}; + +type AccordionItemContextValue = { readonly id: string }; + +const AccordionContext = createContext( + undefined, +); +const AccordionItemContext = createContext< + AccordionItemContextValue | undefined +>(undefined); + +function useAccordion(): AccordionContextValue { + const context = use(AccordionContext); + if (!context) throw new Error("AccordionItem must be used within Accordion"); + return context; +} + +function useAccordionItem(): AccordionItemContextValue { + const context = use(AccordionItemContext); + if (!context) + throw new Error("Accordion parts must be used within AccordionItem"); + return context; +} + +const styles = StyleSheet.create({ + content: { paddingBottom: 16, paddingHorizontal: 16 }, + item: { borderBottomWidth: 1 }, + root: { borderWidth: 1, overflow: "hidden" }, + triggerContent: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + width: "100%", + }, +}); + +/** Native accordion supporting single or concurrent controlled disclosures. */ +function Accordion({ + children, + defaultOpenIds = [], + onOpenIdsChange, + openIds, + reducedMotionService, + ref, + style, + type = "single", + ...props +}: AccordionProps) { + const theme = useTheme(); + const [expandedIds, setExpandedIds] = useControllableState( + openIds === undefined + ? { + defaultValue: defaultOpenIds, + mode: "uncontrolled", + onChange: onOpenIdsChange, + } + : { mode: "controlled", onChange: onOpenIdsChange, value: openIds }, + ); + const toggle = useCallback( + (id: string) => { + const open = expandedIds.includes(id); + const next = open + ? expandedIds.filter((candidate) => candidate !== id) + : type === "single" + ? [id] + : [...expandedIds, id]; + setExpandedIds(next); + }, + [expandedIds, setExpandedIds, type], + ); + const value = useMemo( + () => ({ + isOpen: (id: string) => expandedIds.includes(id), + reducedMotionService, + toggle, + }), + [expandedIds, reducedMotionService, toggle], + ); + + return ( + + + {children} + + + ); +} +Accordion.displayName = "Accordion"; + +/** Caller-identified item boundary for native accordion parts. */ +function AccordionItem({ + children, + id, + ref, + style, + ...props +}: AccordionItemProps) { + const accordion = useAccordion(); + const item = useMemo(() => ({ id }), [id]); + return ( + + { + accordion.toggle(id); + }} + open={accordion.isOpen(id)} + reducedMotionService={accordion.reducedMotionService} + > + + {children} + + + + ); +} +AccordionItem.displayName = "AccordionItem"; + +/** Localized 44-point accordion disclosure trigger. */ +function AccordionTrigger({ + icon, + label, + style, + ...props +}: AccordionTriggerProps) { + useAccordionItem(); + const theme = useTheme(); + return ( + [ + { paddingHorizontal: theme.spacing[4] }, + typeof style === "function" ? style(state) : style, + ]} + > + + + {label} + + {icon} + + + ); +} +AccordionTrigger.displayName = "AccordionTrigger"; + +/** Accordion panel removed from native layout while collapsed. */ +function AccordionContent({ style, ...props }: AccordionContentProps) { + useAccordionItem(); + const theme = useTheme(); + return ( + + ); +} +AccordionContent.displayName = "AccordionContent"; + +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }; diff --git a/packages/ui-native/src/components/animated-list/animated-list.tsx b/packages/ui-native/src/components/animated-list/animated-list.tsx new file mode 100644 index 00000000..0f653582 --- /dev/null +++ b/packages/ui-native/src/components/animated-list/animated-list.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { type ReactNode, type Ref, useEffect, useState } from "react"; + +import { Animated, StyleSheet, View, type ViewProps } from "react-native"; + +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-identified content rendered by a native animated list. */ +export type AnimatedListItem = { + readonly content: ReactNode; + readonly id: string; +}; + +/** Props for a reduced-motion-aware native entrance list. */ +export type AnimatedListProps = Omit & { + readonly delay?: number; + readonly items: readonly AnimatedListItem[]; + readonly label: string; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; +}; + +const styles = StyleSheet.create({ root: { width: "100%" } }); + +function AnimatedListRow({ + content, + delay, + index, + reduceMotion, +}: { + readonly content: ReactNode; + readonly delay: number; + readonly index: number; + readonly reduceMotion: boolean; +}) { + const [progress, setProgress] = useState( + () => new Animated.Value(reduceMotion ? 1 : 0), + ); + void setProgress; + useEffect(() => { + Animated.timing(progress, { + delay: reduceMotion ? 0 : index * delay, + duration: reduceMotion ? 0 : 100, + toValue: 1, + useNativeDriver: true, + }).start(); + }, [delay, index, progress, reduceMotion]); + + return ( + + {content} + + ); +} +AnimatedListRow.displayName = "AnimatedListRow"; + +/** Purposeful one-time list entrances that collapse to no motion when requested. */ +function AnimatedList({ + delay = 40, + items, + label, + reducedMotionService, + ref, + style, + ...props +}: AnimatedListProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + return ( + + {items.map((item, index) => ( + + ))} + + ); +} +AnimatedList.displayName = "AnimatedList"; + +export { AnimatedList }; diff --git a/packages/ui-native/src/components/animated-testimonials/animated-testimonials.tsx b/packages/ui-native/src/components/animated-testimonials/animated-testimonials.tsx new file mode 100644 index 00000000..df4af9fd --- /dev/null +++ b/packages/ui-native/src/components/animated-testimonials/animated-testimonials.tsx @@ -0,0 +1,250 @@ +"use client"; + +import { useCallback, useEffect, useState } from "react"; + +import type { Ref } from "react"; +import { + Animated, + Pressable, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-identified testimonial. */ +export type AnimatedTestimonial = { + readonly id: string; + readonly name: string; + readonly quote: string; + readonly title: string; +}; + +/** Localized labels for testimonial navigation. */ +export type AnimatedTestimonialsLabels = { + readonly next: string; + readonly position: (index: number, total: number) => string; + readonly previous: string; + readonly region: string; +}; + +/** Props for controlled or uncontrolled native testimonial rotation. */ +export type AnimatedTestimonialsProps = Omit & { + readonly autoplay?: boolean; + readonly autoplayInterval?: number; + readonly defaultSelectedId?: string; + readonly labels: AnimatedTestimonialsLabels; + readonly onSelectedIdChange?: (id: string) => void; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly selectedId?: string; + readonly testimonials: readonly AnimatedTestimonial[]; +}; + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + actions: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + }, + root: { borderWidth: 1 }, +}); + +function TestimonialCard({ + reduceMotion, + testimonial, +}: { + readonly reduceMotion: boolean; + readonly testimonial: AnimatedTestimonial; +}) { + const theme = useTheme(); + const [progress, setProgress] = useState( + () => new Animated.Value(reduceMotion ? 1 : 0), + ); + void setProgress; + useEffect(() => { + Animated.timing(progress, { + duration: reduceMotion ? 0 : 100, + toValue: 1, + useNativeDriver: true, + }).start(); + }, [progress, reduceMotion]); + return ( + + + {testimonial.quote} + + + {testimonial.name} + + + {testimonial.title} + + + ); +} +TestimonialCard.displayName = "TestimonialCard"; + +/** Native testimonial pager that disables automatic movement for reduced motion. */ +function AnimatedTestimonials({ + autoplay = false, + autoplayInterval = 5000, + defaultSelectedId, + labels, + onSelectedIdChange, + reducedMotionService, + ref, + selectedId, + style, + testimonials, + ...props +}: AnimatedTestimonialsProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [selection, setSelection] = useControllableState( + selectedId === undefined + ? { + defaultValue: defaultSelectedId ?? testimonials[0]?.id ?? "", + mode: "uncontrolled", + onChange: onSelectedIdChange, + } + : { mode: "controlled", onChange: onSelectedIdChange, value: selectedId }, + ); + const selectedIndex = Math.max( + 0, + testimonials.findIndex((item) => item.id === selection), + ); + const active = testimonials[selectedIndex]; + const move = useCallback( + (step: number) => { + if (testimonials.length === 0) return; + const nextIndex = + (selectedIndex + step + testimonials.length) % testimonials.length; + const next = testimonials[nextIndex]; + if (next) setSelection(next.id); + }, + [selectedIndex, setSelection, testimonials], + ); + + useEffect(() => { + if (!autoplay || reduceMotion || testimonials.length <= 1) return; + const timer = setInterval( + () => { + move(1); + }, + Math.max(1000, autoplayInterval), + ); + return () => { + clearInterval(timer); + }; + }, [autoplay, autoplayInterval, move, reduceMotion, testimonials.length]); + + if (!active) return null; + const controlsDisabled = testimonials.length <= 1; + return ( + + + + { + move(-1); + }} + style={styles.action} + > + + {labels.previous} + + + + {labels.position(selectedIndex + 1, testimonials.length)} + + { + move(1); + }} + style={styles.action} + > + {labels.next} + + + + ); +} +AnimatedTestimonials.displayName = "AnimatedTestimonials"; + +export { AnimatedTestimonials }; diff --git a/packages/ui-native/src/components/carousel/carousel.tsx b/packages/ui-native/src/components/carousel/carousel.tsx new file mode 100644 index 00000000..ff247c04 --- /dev/null +++ b/packages/ui-native/src/components/carousel/carousel.tsx @@ -0,0 +1,233 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; + +import type { ReactNode, Ref } from "react"; +import { + Pressable, + ScrollView, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-identified native carousel slide. */ +export type CarouselItem = { + readonly content: ReactNode; + readonly id: string; + readonly label: string; +}; + +/** Localized labels for native carousel controls and position. */ +export type CarouselLabels = { + readonly next: string; + readonly position: (index: number, total: number) => string; + readonly previous: string; + readonly region: string; +}; + +/** Props for the swipeable, controlled or uncontrolled native carousel. */ +export type CarouselProps = Omit & { + readonly defaultSelectedId?: string; + readonly items: readonly CarouselItem[]; + readonly labels: CarouselLabels; + readonly loop?: boolean; + readonly onSelectedIdChange?: (id: string) => void; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly selectedId?: string; +}; + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + actions: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + }, + root: { overflow: "hidden", width: "100%" }, + slides: { flexDirection: "row" }, +}); + +function targetIndex({ + count, + current, + loop, + step, +}: { + readonly count: number; + readonly current: number; + readonly loop: boolean; + readonly step: number; +}): number { + if (count === 0) return 0; + if (loop) return (current + step + count) % count; + return Math.min(count - 1, Math.max(0, current + step)); +} + +/** + * Native horizontal paging surface with swipe gestures and accessible actions. + * The host supplies stable slide ids and localized control text. + */ +function Carousel({ + defaultSelectedId, + items, + labels, + loop = false, + onSelectedIdChange, + reducedMotionService, + ref, + selectedId, + style, + ...props +}: CarouselProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const scrollRef = useRef(null); + const [width, setWidth] = useState(0); + const [selection, setSelection] = useControllableState( + selectedId === undefined + ? { + defaultValue: defaultSelectedId ?? items[0]?.id ?? "", + mode: "uncontrolled", + onChange: onSelectedIdChange, + } + : { mode: "controlled", onChange: onSelectedIdChange, value: selectedId }, + ); + const foundIndex = items.findIndex((item) => item.id === selection); + const selectedIndex = foundIndex < 0 ? 0 : foundIndex; + const move = useCallback( + (step: number) => { + const next = + items[ + targetIndex({ + count: items.length, + current: selectedIndex, + loop, + step, + }) + ]; + if (next) setSelection(next.id); + }, + [items, loop, selectedIndex, setSelection], + ); + + useEffect(() => { + if (width <= 0) return; + scrollRef.current?.scrollTo({ + animated: !reduceMotion, + x: selectedIndex * width, + y: 0, + }); + }, [reduceMotion, selectedIndex, width]); + + const previousDisabled = items.length <= 1 || (!loop && selectedIndex === 0); + const nextDisabled = + items.length <= 1 || (!loop && selectedIndex === items.length - 1); + const position = + items.length === 0 + ? labels.position(0, 0) + : labels.position(selectedIndex + 1, items.length); + return ( + { + if (event.nativeEvent.actionName === "decrement") move(-1); + if (event.nativeEvent.actionName === "increment") move(1); + }} + onLayout={(event) => { + setWidth(event.nativeEvent.layout.width); + }} + ref={ref} + style={[styles.root, style]} + > + { + if (width <= 0) return; + const index = Math.round(event.nativeEvent.contentOffset.x / width); + const item = items[index]; + if (item) setSelection(item.id); + }} + pagingEnabled + ref={scrollRef} + showsHorizontalScrollIndicator={false} + > + + {items.map((item, index) => ( + 0 ? width : undefined }} + > + {item.content} + + ))} + + + + { + move(-1); + }} + style={styles.action} + > + + {labels.previous} + + + + {position} + + { + move(1); + }} + style={styles.action} + > + {labels.next} + + + + ); +} +Carousel.displayName = "Carousel"; + +export { Carousel }; diff --git a/packages/ui-native/src/components/collapsible/collapsible.tsx b/packages/ui-native/src/components/collapsible/collapsible.tsx new file mode 100644 index 00000000..938267c0 --- /dev/null +++ b/packages/ui-native/src/components/collapsible/collapsible.tsx @@ -0,0 +1,219 @@ +"use client"; + +import { + createContext, + type ReactNode, + type Ref, + use, + useEffect, + useMemo, + useState, +} from "react"; + +import { + Animated, + Pressable, + type PressableProps, + StyleSheet, + View, + type ViewProps, +} from "react-native"; + +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for controlled or uncontrolled native collapsible content. */ +export type CollapsibleProps = Omit & { + readonly children: ReactNode; + readonly defaultOpen?: boolean; + readonly id: string; + readonly onOpenChange?: (open: boolean) => void; + readonly open?: boolean; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; +}; + +/** Props for the native collapsible disclosure button. */ +export type CollapsibleTriggerProps = Omit< + PressableProps, + "accessibilityLabel" | "children" | "onPress" | "ref" +> & { + readonly children: ReactNode; + readonly label: string; + readonly ref?: Ref; +}; + +/** Props for content mounted while its native collapsible is open. */ +export type CollapsibleContentProps = Omit & { + readonly children: ReactNode; + readonly ref?: Ref; +}; + +type CollapsibleContextValue = { + readonly id: string; + readonly open: boolean; + readonly reduceMotion: boolean; + readonly toggle: () => void; +}; + +const CollapsibleContext = createContext( + undefined, +); + +function useCollapsible(): CollapsibleContextValue { + const context = use(CollapsibleContext); + if (!context) + throw new Error("Collapsible parts must be used within Collapsible"); + return context; +} + +const styles = StyleSheet.create({ + trigger: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, +}); + +/** Native disclosure state boundary with caller-owned stable identifiers. */ +function Collapsible({ + children, + defaultOpen = false, + id, + onOpenChange, + open, + reducedMotionService, + ref, + style, + ...props +}: CollapsibleProps) { + const [expanded, setExpanded] = useControllableState( + open === undefined + ? { + defaultValue: defaultOpen, + mode: "uncontrolled", + onChange: onOpenChange, + } + : { mode: "controlled", onChange: onOpenChange, value: open }, + ); + const reduceMotion = useReducedMotion(reducedMotionService); + const value = useMemo( + () => ({ + id, + open: expanded, + reduceMotion, + toggle: () => { + setExpanded(!expanded); + }, + }), + [expanded, id, reduceMotion, setExpanded], + ); + return ( + + + {children} + + + ); +} +Collapsible.displayName = "Collapsible"; + +/** Accessible 44-point disclosure control for native collapsible content. */ +function CollapsibleTrigger({ + children, + disabled = false, + label, + ref, + style, + ...props +}: CollapsibleTriggerProps) { + const theme = useTheme(); + const collapsible = useCollapsible(); + const handlePress = () => { + collapsible.toggle(); + }; + return ( + [ + styles.trigger, + { + backgroundColor: state.pressed + ? theme.colors.accent + : theme.colors.background, + borderRadius: theme.radius.md, + opacity: disabled ? 0.5 : state.pressed ? 0.8 : 1, + }, + typeof style === "function" ? style(state) : style, + ]} + > + {children} + + ); +} +CollapsibleTrigger.displayName = "CollapsibleTrigger"; + +/** Collapsed content removed from layout and accessibility when closed. */ +function CollapsibleContent({ + children, + ref, + style, + ...props +}: CollapsibleContentProps) { + const collapsible = useCollapsible(); + const [progress, setProgress] = useState( + () => new Animated.Value(collapsible.reduceMotion ? 1 : 0), + ); + void setProgress; + useEffect(() => { + if (!collapsible.open) return; + Animated.timing(progress, { + duration: collapsible.reduceMotion ? 0 : 100, + toValue: 1, + useNativeDriver: true, + }).start(); + }, [collapsible.open, collapsible.reduceMotion, progress]); + + if (!collapsible.open) return null; + return ( + + {children} + + ); +} +CollapsibleContent.displayName = "CollapsibleContent"; + +export { Collapsible, CollapsibleContent, CollapsibleTrigger }; diff --git a/packages/ui-native/src/components/expandable-cards/expandable-cards.tsx b/packages/ui-native/src/components/expandable-cards/expandable-cards.tsx new file mode 100644 index 00000000..e5661a8e --- /dev/null +++ b/packages/ui-native/src/components/expandable-cards/expandable-cards.tsx @@ -0,0 +1,159 @@ +"use client"; + +import type { ReactNode, Ref } from "react"; +import { StyleSheet, Text, View, type ViewProps } from "react-native"; + +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "../collapsible/collapsible"; + +/** Caller-identified expandable native card. */ +export type ExpandableCardItem = { + readonly content: ReactNode; + readonly description?: string; + readonly id: string; + readonly title: string; +}; + +/** Localized labels for expandable cards. */ +export type ExpandableCardsLabels = { + readonly collapseCard: (item: ExpandableCardItem) => string; + readonly expandCard: (item: ExpandableCardItem) => string; + readonly region: string; +}; + +/** Props for a controlled or uncontrolled stack of native cards. */ +export type ExpandableCardsProps = Omit & { + readonly cards: readonly ExpandableCardItem[]; + readonly defaultExpandedId?: null | string; + readonly expandedId?: null | string; + readonly labels: ExpandableCardsLabels; + readonly onExpandedIdChange?: (id: null | string) => void; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; +}; + +const styles = StyleSheet.create({ + card: { borderWidth: 1, overflow: "hidden" }, + root: { width: "100%" }, + trigger: { alignItems: "flex-start", width: "100%" }, +}); + +function ExpandableCard({ + card, + expanded, + labels, + onExpandedChange, + reducedMotionService, +}: { + readonly card: ExpandableCardItem; + readonly expanded: boolean; + readonly labels: ExpandableCardsLabels; + readonly onExpandedChange: (expanded: boolean) => void; + readonly reducedMotionService?: ReducedMotionService; +}) { + const theme = useTheme(); + return ( + + + + + {card.title} + + {card.description === undefined ? null : ( + + {card.description} + + )} + + + + {card.content} + + + ); +} +ExpandableCard.displayName = "ExpandableCard"; + +/** Native single-expansion card stack with caller-owned card identities. */ +function ExpandableCards({ + cards, + defaultExpandedId = null, + expandedId, + labels, + onExpandedIdChange, + reducedMotionService, + ref, + style, + ...props +}: ExpandableCardsProps) { + const theme = useTheme(); + const [expanded, setExpanded] = useControllableState( + expandedId === undefined + ? { + defaultValue: defaultExpandedId, + mode: "uncontrolled", + onChange: onExpandedIdChange, + } + : { mode: "controlled", onChange: onExpandedIdChange, value: expandedId }, + ); + return ( + + {cards.map((card) => ( + { + setExpanded(next ? card.id : null); + }} + reducedMotionService={reducedMotionService} + /> + ))} + + ); +} +ExpandableCards.displayName = "ExpandableCards"; + +export { ExpandableCards }; diff --git a/packages/ui-native/src/components/faq/faq.tsx b/packages/ui-native/src/components/faq/faq.tsx new file mode 100644 index 00000000..49495276 --- /dev/null +++ b/packages/ui-native/src/components/faq/faq.tsx @@ -0,0 +1,177 @@ +"use client"; + +import type { ReactNode, Ref } from "react"; +import { StyleSheet, Text, View, type ViewProps } from "react-native"; + +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "../collapsible/collapsible"; + +/** Caller-identified native FAQ entry. */ +export type FAQItem = { + readonly answer: ReactNode; + readonly id: string; + readonly question: string; +}; + +/** Localized labels for the FAQ and its disclosure actions. */ +export type FAQLabels = { + readonly collapseAnswer: (item: FAQItem) => string; + readonly expandAnswer: (item: FAQItem) => string; + readonly region: string; +}; + +/** Props for a native FAQ with controlled or uncontrolled open answers. */ +export type FAQProps = Omit & { + readonly defaultOpenIds?: readonly string[]; + readonly items: readonly FAQItem[]; + readonly labels: FAQLabels; + readonly onOpenIdsChange?: (ids: readonly string[]) => void; + readonly openIds?: readonly string[]; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly title: string; +}; + +const styles = StyleSheet.create({ + header: { borderBottomWidth: 1 }, + item: { borderBottomWidth: 1 }, + root: { borderWidth: 1, overflow: "hidden" }, +}); + +function FAQRow({ + item, + labels, + onOpenChange, + open, + reducedMotionService, +}: { + readonly item: FAQItem; + readonly labels: FAQLabels; + readonly onOpenChange: (open: boolean) => void; + readonly open: boolean; + readonly reducedMotionService?: ReducedMotionService; +}) { + const theme = useTheme(); + return ( + + + + {item.question} + + + + {item.answer} + + + ); +} +FAQRow.displayName = "FAQRow"; + +/** Native FAQ disclosure list with no hard-coded product copy. */ +function FAQ({ + defaultOpenIds = [], + items, + labels, + onOpenIdsChange, + openIds, + reducedMotionService, + ref, + style, + title, + ...props +}: FAQProps) { + const theme = useTheme(); + const [open, setOpen] = useControllableState( + openIds === undefined + ? { + defaultValue: defaultOpenIds, + mode: "uncontrolled", + onChange: onOpenIdsChange, + } + : { mode: "controlled", onChange: onOpenIdsChange, value: openIds }, + ); + return ( + + + + {title} + + + + {items.map((item) => ( + { + const ids = next + ? [...open, item.id] + : open.filter((id) => id !== item.id); + setOpen(ids); + }} + open={open.includes(item.id)} + reducedMotionService={reducedMotionService} + /> + ))} + + + ); +} +FAQ.displayName = "FAQ"; + +export { FAQ }; diff --git a/packages/ui-native/src/components/floating-toolbar/floating-toolbar.tsx b/packages/ui-native/src/components/floating-toolbar/floating-toolbar.tsx new file mode 100644 index 00000000..40a539ce --- /dev/null +++ b/packages/ui-native/src/components/floating-toolbar/floating-toolbar.tsx @@ -0,0 +1,149 @@ +import type { ReactNode, Ref } from "react"; +import { + Pressable, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** One caller-identified native toolbar action. */ +export type FloatingToolbarAction = { + readonly disabled?: boolean; + readonly icon?: ReactNode; + readonly id: string; + readonly label: string; + readonly onPress: () => void; + readonly variant?: "destructive" | "primary" | "secondary"; +}; + +/** Localized labels for a floating toolbar. */ +export type FloatingToolbarLabels = { + readonly region: string; +}; + +/** Props for a toolbar positioned in its nearest native layout container. */ +export type FloatingToolbarProps = Omit & { + readonly actions: readonly FloatingToolbarAction[]; + readonly labels: FloatingToolbarLabels; + readonly ref?: Ref; + readonly x: number; + readonly y: number; +}; + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + flexDirection: "row", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + root: { + alignItems: "center", + borderWidth: 1, + flexDirection: "row", + position: "absolute", + }, +}); + +function actionColors( + theme: ReturnType, + variant: NonNullable, +) { + if (variant === "primary") { + return { + backgroundColor: theme.colors.primary, + color: theme.colors.primaryForeground, + }; + } + if (variant === "destructive") { + return { + backgroundColor: theme.colors.destructive, + color: theme.colors.destructiveForeground, + }; + } + return { + backgroundColor: theme.colors.secondary, + color: theme.colors.secondaryForeground, + }; +} + +/** Compact RN-core action bar with explicit host-owned coordinates. */ +function FloatingToolbar({ + actions, + labels, + ref, + style, + x, + y, + ...props +}: FloatingToolbarProps) { + const theme = useTheme(); + return ( + + {actions.map((action) => { + const colors = actionColors(theme, action.variant ?? "secondary"); + const handlePress = () => { + action.onPress(); + }; + return ( + [ + styles.action, + { + backgroundColor: colors.backgroundColor, + borderRadius: theme.radius.md, + gap: theme.spacing[2], + opacity: action.disabled ? 0.5 : pressed ? 0.8 : 1, + paddingHorizontal: theme.spacing[3], + }, + ]} + > + {action.icon} + + {action.label} + + + ); + })} + + ); +} +FloatingToolbar.displayName = "FloatingToolbar"; + +export { FloatingToolbar }; diff --git a/packages/ui-native/src/components/interactive-data-content-native.test.tsx b/packages/ui-native/src/components/interactive-data-content-native.test.tsx new file mode 100644 index 00000000..cf294a27 --- /dev/null +++ b/packages/ui-native/src/components/interactive-data-content-native.test.tsx @@ -0,0 +1,319 @@ +import { fireEvent, render, screen } from "@testing-library/react-native"; +import { Text, View } from "react-native"; + +import type { ReducedMotionService } from "../primitives/use-reduced-motion"; + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "./accordion/accordion"; +import { AnimatedList } from "./animated-list/animated-list"; +import { AnimatedTestimonials } from "./animated-testimonials/animated-testimonials"; +import { Carousel } from "./carousel/carousel"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "./collapsible/collapsible"; +import { ExpandableCards } from "./expandable-cards/expandable-cards"; +import { FAQ as Faq } from "./faq/faq"; +import { FloatingToolbar } from "./floating-toolbar/floating-toolbar"; +import { InteractiveTimeline } from "./interactive-timeline/interactive-timeline"; +import { + calculateScrollProgress, + ScrollProgress, +} from "./scroll-progress/scroll-progress"; +import { Slideshow } from "./slideshow/slideshow"; +import { TreeView } from "./tree-view/tree-view"; + +const reducedMotionService: ReducedMotionService = { + addEventListener: (_eventName, listener) => { + listener(true); + return { remove: jest.fn() }; + }, + isReduceMotionEnabled: () => + new Promise((resolve) => { + void resolve; + }), +}; + +const pagingLabels = { + next: "Next item", + position: (index: number, total: number) => `${index} of ${total}`, + previous: "Previous item", + region: "Featured items", +}; + +describe("native interactive data and content components", () => { + it("calculates and renders caller-driven scroll progress", () => { + expect( + calculateScrollProgress({ + contentOffset: { y: 300 }, + contentSize: { height: 1000 }, + layoutMeasurement: { height: 400 }, + }), + ).toBe(0.5); + render(); + expect(screen.getByLabelText("Article progress")).toHaveProp( + "accessibilityValue", + { max: 100, min: 0, now: 50 }, + ); + }); + + it("invokes floating toolbar actions with disabled semantics", () => { + const rename = jest.fn(); + render( + , + ); + fireEvent.press(screen.getByRole("button", { name: "Rename item" })); + expect(rename).toHaveBeenCalledTimes(1); + expect(screen.getByRole("button", { name: "Delete item" })).toBeDisabled(); + }); + + it("filters and selects caller-identified timeline events", () => { + const onSelectedIdChange = jest.fn(); + render( + date.toISOString().slice(0, 10)} + labels={{ + region: "Product timeline", + zoomIn: "Zoom in", + zoomOut: "Zoom out", + }} + onSelectedIdChange={onSelectedIdChange} + startDate={new Date("2026-01-01")} + tracks={[{ id: "product", label: "Product" }]} + />, + ); + fireEvent.press( + screen.getByRole("button", { name: "Version one, 2026-06-01" }), + ); + expect(onSelectedIdChange).toHaveBeenCalledWith("v1"); + fireEvent.press(screen.getByRole("checkbox", { name: "Releases" })); + expect( + screen.queryByRole("button", { name: "Version one, 2026-06-01" }), + ).toBeNull(); + }); + + it("expands branches and supports multiple tree selection", () => { + const onSelectedIdsChange = jest.fn(); + render( + `Collapse ${node.label}`, + expandNode: (node) => `Expand ${node.label}`, + region: "Project files", + }} + nodes={[ + { + id: "src", + label: "Source", + nodes: [{ id: "button", label: "Button file" }], + }, + ]} + onSelectedIdsChange={onSelectedIdsChange} + selectionMode="multiple" + />, + ); + fireEvent.press(screen.getByRole("button", { name: "Expand Source" })); + fireEvent.press(screen.getByRole("button", { name: "Button file" })); + expect(onSelectedIdsChange).toHaveBeenCalledWith(["button"]); + expect(screen.getByRole("button", { name: "Button file" })).toHaveProp( + "accessibilityState", + { disabled: undefined, selected: true }, + ); + }); + + it("shares controlled disclosure behavior across accordion and collapsible", () => { + const onOpenIdsChange = jest.fn(); + render( + + + + + + Accordion details + + + + + + Notes + + + Native notes + + + , + ); + fireEvent.press(screen.getByRole("button", { name: "Show details" })); + expect(onOpenIdsChange).toHaveBeenCalledWith(["details"]); + expect(screen.queryByText("Accordion details")).toBeNull(); + fireEvent.press(screen.getByRole("button", { name: "Show notes" })); + expect(screen.getByText("Native notes")).toBeOnTheScreen(); + }); + + it("renders stable animated list items and advances testimonials", () => { + render( + + First update, id: "first" }, + { content: Second update, id: "second" }, + ]} + label="Updates" + reducedMotionService={reducedMotionService} + /> + + , + ); + expect(screen.getByText("First update")).toBeOnTheScreen(); + expect(screen.getByText("Second update")).toBeOnTheScreen(); + fireEvent.press(screen.getByRole("button", { name: "Next item" })); + expect(screen.getByText("Second quote")).toBeOnTheScreen(); + }); + + it("pages native carousel content and preserves controlled ownership", () => { + const onSelectedIdChange = jest.fn(); + render( + Alpha slide, id: "alpha", label: "Alpha" }, + { content: Beta slide, id: "beta", label: "Beta" }, + ]} + labels={pagingLabels} + onSelectedIdChange={onSelectedIdChange} + reducedMotionService={reducedMotionService} + selectedId="alpha" + />, + ); + fireEvent.press(screen.getByRole("button", { name: "Next item" })); + expect(onSelectedIdChange).toHaveBeenCalledWith("beta"); + expect(screen.getByText("1 of 2")).toBeOnTheScreen(); + }); + + it("expands cards and FAQ answers with localized labels", () => { + render( + + Card body, + id: "card", + title: "Card title", + }, + ]} + labels={{ + collapseCard: (item) => `Collapse ${item.title}`, + expandCard: (item) => `Expand ${item.title}`, + region: "Cards", + }} + reducedMotionService={reducedMotionService} + /> + Because it is native., + id: "why", + question: "Why?", + }, + ]} + labels={{ + collapseAnswer: (item) => `Collapse ${item.question}`, + expandAnswer: (item) => `Expand ${item.question}`, + region: "Questions", + }} + reducedMotionService={reducedMotionService} + title="Common questions" + /> + , + ); + fireEvent.press(screen.getByRole("button", { name: "Expand Card title" })); + fireEvent.press(screen.getByRole("button", { name: "Expand Why?" })); + expect(screen.getByText("Card body")).toBeOnTheScreen(); + expect(screen.getByText("Because it is native.")).toBeOnTheScreen(); + }); + + it("uses the shared native modal layer for slideshow navigation", () => { + const onCurrentSectionIdChange = jest.fn(); + const onComplete = jest.fn(); + render( + `${index} of ${total}`, + previous: "Previous section", + sections: "Tutorial sections", + }} + onComplete={onComplete} + onCurrentSectionIdChange={onCurrentSectionIdChange} + onToggleComplete={jest.fn()} + reducedMotionService={reducedMotionService} + sections={[ + { + content: Introduction content, + id: "intro", + title: "Introduction", + }, + { + content: Finish content, + id: "finish", + title: "Finish", + }, + ]} + title="Native tutorial" + />, + ); + fireEvent.press(screen.getByRole("button", { name: "Next section" })); + expect(onCurrentSectionIdChange).toHaveBeenCalledWith("finish"); + expect(screen.getByText("Finish content")).toBeOnTheScreen(); + fireEvent.press(screen.getByRole("button", { name: "Finish tutorial" })); + expect(onComplete).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/ui-native/src/components/interactive-timeline/interactive-timeline.tsx b/packages/ui-native/src/components/interactive-timeline/interactive-timeline.tsx new file mode 100644 index 00000000..926e2713 --- /dev/null +++ b/packages/ui-native/src/components/interactive-timeline/interactive-timeline.tsx @@ -0,0 +1,419 @@ +"use client"; + +import { useCallback, useState } from "react"; + +import type { Ref } from "react"; +import { + Pressable, + ScrollView, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { + isSingleSelected, + toggleMultipleSelected, +} from "../../primitives/selection"; +import { useControllableState } from "../../primitives/use-controllable-state"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-identified timeline lane. */ +export type InteractiveTimelineTrack = { + readonly id: string; + readonly label: string; +}; + +/** Caller-identified filter category. */ +export type InteractiveTimelineCategory = { + readonly id: string; + readonly label: string; +}; + +/** Point or duration rendered in a native timeline lane. */ +export type InteractiveTimelineEvent = { + readonly categoryId?: string; + readonly description?: string; + readonly endDate?: Date; + readonly id: string; + readonly startDate: Date; + readonly title: string; + readonly trackId: string; +}; + +/** Localized labels for native timeline controls. */ +export type InteractiveTimelineLabels = { + readonly region: string; + readonly zoomIn: string; + readonly zoomOut: string; +}; + +/** Props for the scrollable native interactive timeline. */ +export type InteractiveTimelineProps = Omit & { + readonly categories?: readonly InteractiveTimelineCategory[]; + readonly defaultSelectedId?: string; + readonly defaultVisibleCategoryIds?: readonly string[]; + readonly defaultZoom?: number; + readonly endDate: Date; + readonly events: readonly InteractiveTimelineEvent[]; + readonly formatDate: (date: Date) => string; + readonly labels: InteractiveTimelineLabels; + readonly onEventPress?: (event: InteractiveTimelineEvent) => void; + readonly onSelectedIdChange?: (id: string) => void; + readonly onVisibleCategoryIdsChange?: (ids: readonly string[]) => void; + readonly onZoomChange?: (zoom: number) => void; + readonly ref?: Ref; + readonly selectedId?: string; + readonly startDate: Date; + readonly tracks: readonly InteractiveTimelineTrack[]; + readonly visibleCategoryIds?: readonly string[]; + readonly zoom?: number; +}; + +type TimelineLaneProps = { + readonly end: number; + readonly events: readonly InteractiveTimelineEvent[]; + readonly formatDate: (date: Date) => string; + readonly onSelect: (event: InteractiveTimelineEvent) => void; + readonly selectedId?: string; + readonly start: number; + readonly track: InteractiveTimelineTrack; + readonly width: number; +}; + +const styles = StyleSheet.create({ + category: { alignItems: "center", justifyContent: "center", minHeight: 44 }, + categoryRow: { flexDirection: "row" }, + event: { + justifyContent: "center", + minHeight: 44, + minWidth: 44, + position: "absolute", + }, + lane: { borderTopWidth: 1, height: 64, position: "relative" }, + laneLabel: { left: 0, position: "absolute", top: 0, zIndex: 1 }, + root: { borderWidth: 1, overflow: "hidden" }, + toolbar: { alignItems: "center", flexDirection: "row" }, + zoom: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, +}); + +function clampedZoom(value: number): number { + return Math.min(8, Math.max(1, value)); +} + +function eventGeometry( + event: InteractiveTimelineEvent, + start: number, + end: number, +) { + const span = Math.max(1, end - start); + const eventStart = Math.min( + 1, + Math.max(0, (event.startDate.getTime() - start) / span), + ); + const eventEnd = event.endDate + ? Math.min( + 1, + Math.max(eventStart, (event.endDate.getTime() - start) / span), + ) + : eventStart; + return { + left: eventStart, + width: event.endDate ? Math.max(0.02, eventEnd - eventStart) : 0, + }; +} + +function TimelineLane({ + end, + events, + formatDate, + onSelect, + selectedId, + start, + track, + width, +}: TimelineLaneProps) { + const theme = useTheme(); + return ( + + + {track.label} + + {events.map((event) => { + const geometry = eventGeometry(event, start, end); + const selected = isSingleSelected( + selectedId, + event, + (candidate) => candidate.id, + ); + return ( + { + onSelect(event); + }} + style={({ pressed }) => [ + styles.event, + { + backgroundColor: selected + ? theme.colors.primary + : theme.colors.accent, + borderColor: selected ? theme.colors.ring : theme.colors.border, + borderRadius: theme.radius.sm, + borderWidth: 1, + left: geometry.left * width, + opacity: pressed ? 0.8 : 1, + paddingHorizontal: theme.spacing[2], + top: theme.spacing[4], + width: + geometry.width > 0 + ? Math.max(44, geometry.width * width) + : 44, + }, + ]} + > + + {event.title} + + + ); + })} + + ); +} +TimelineLane.displayName = "TimelineLane"; + +/** + * Horizontally scrollable native timeline with filter, selection, and zoom + * state. Pinch and browser-style pointer panning are intentionally not claimed. + */ +function InteractiveTimeline({ + categories = [], + defaultSelectedId, + defaultVisibleCategoryIds, + defaultZoom = 1, + endDate, + events, + formatDate, + labels, + onEventPress, + onSelectedIdChange, + onVisibleCategoryIdsChange, + onZoomChange, + ref, + selectedId, + startDate, + style, + tracks, + visibleCategoryIds, + zoom, + ...props +}: InteractiveTimelineProps) { + const theme = useTheme(); + const [layoutWidth, setLayoutWidth] = useState(1); + const [selection, setSelection] = useControllableState( + selectedId === undefined + ? { + defaultValue: defaultSelectedId ?? "", + mode: "uncontrolled", + onChange: onSelectedIdChange, + } + : { mode: "controlled", onChange: onSelectedIdChange, value: selectedId }, + ); + const defaultCategories = + defaultVisibleCategoryIds ?? categories.map((category) => category.id); + const [visible, setVisible] = useControllableState( + visibleCategoryIds === undefined + ? { + defaultValue: defaultCategories, + mode: "uncontrolled", + onChange: onVisibleCategoryIdsChange, + } + : { + mode: "controlled", + onChange: onVisibleCategoryIdsChange, + value: visibleCategoryIds, + }, + ); + const [scale, setScale] = useControllableState( + zoom === undefined + ? { + defaultValue: clampedZoom(defaultZoom), + mode: "uncontrolled", + onChange: onZoomChange, + } + : { + mode: "controlled", + onChange: onZoomChange, + value: clampedZoom(zoom), + }, + ); + const selectEvent = useCallback( + (event: InteractiveTimelineEvent) => { + setSelection(event.id); + onEventPress?.(event); + }, + [onEventPress, setSelection], + ); + const contentWidth = layoutWidth * clampedZoom(scale); + const visibleEvents = events.filter( + (event) => + event.categoryId === undefined || visible.includes(event.categoryId), + ); + + return ( + { + setLayoutWidth(Math.max(1, event.nativeEvent.layout.width)); + }} + ref={ref} + style={[ + styles.root, + { + backgroundColor: theme.colors.background, + borderColor: theme.colors.border, + borderRadius: theme.radius.lg, + }, + style, + ]} + > + + { + setScale(clampedZoom(scale / 2)); + }} + style={styles.zoom} + > + + + = 8 }} + disabled={scale >= 8} + onPress={() => { + setScale(clampedZoom(scale * 2)); + }} + style={styles.zoom} + > + + + + + {categories.map((category) => { + const checked = visible.includes(category.id); + return ( + { + const next = toggleMultipleSelected( + new Set(visible), + category, + (candidate) => candidate.id, + ); + setVisible([...next]); + }} + style={[ + styles.category, + { + backgroundColor: checked + ? theme.colors.secondary + : theme.colors.background, + borderColor: theme.colors.border, + borderRadius: theme.radius.full, + borderWidth: 1, + paddingHorizontal: theme.spacing[3], + }, + ]} + > + + {category.label} + + + ); + })} + + + + + {tracks.map((track) => ( + event.trackId === track.id, + )} + formatDate={formatDate} + key={track.id} + onSelect={selectEvent} + selectedId={selection} + start={startDate.getTime()} + track={track} + width={contentWidth} + /> + ))} + + + + ); +} +InteractiveTimeline.displayName = "InteractiveTimeline"; + +export { InteractiveTimeline }; diff --git a/packages/ui-native/src/components/scroll-progress/scroll-progress.tsx b/packages/ui-native/src/components/scroll-progress/scroll-progress.tsx new file mode 100644 index 00000000..277d294e --- /dev/null +++ b/packages/ui-native/src/components/scroll-progress/scroll-progress.tsx @@ -0,0 +1,82 @@ +import type { Ref } from "react"; +import { StyleSheet, View, type ViewProps } from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Native scroll metrics accepted by {@link calculateScrollProgress}. */ +export type NativeScrollMetrics = { + readonly contentOffset: { readonly y: number }; + readonly contentSize: { readonly height: number }; + readonly layoutMeasurement: { readonly height: number }; +}; + +/** Props for a caller-driven native scroll progress indicator. */ +export type ScrollProgressProps = Omit & { + readonly label: string; + readonly ref?: Ref; + readonly value: number; +}; + +const styles = StyleSheet.create({ + fill: { height: 4 }, + track: { height: 4, overflow: "hidden", width: "100%" }, +}); + +function clampProgress(value: number): number { + return Math.min(1, Math.max(0, value)); +} + +/** Converts a vertical React Native scroll event payload to a zero-to-one value. */ +function calculateScrollProgress(metrics: NativeScrollMetrics): number { + const scrollable = + metrics.contentSize.height - metrics.layoutMeasurement.height; + if (scrollable <= 0) return 0; + return clampProgress(metrics.contentOffset.y / scrollable); +} + +/** + * Progress bar driven by a host ScrollView or FlatList. Native has no global + * document scroll position, so the caller owns and supplies the value. + */ +function ScrollProgress({ + label, + ref, + style, + value, + ...props +}: ScrollProgressProps) { + const theme = useTheme(); + const progress = clampProgress(value); + + return ( + + + + ); +} +ScrollProgress.displayName = "ScrollProgress"; + +export { calculateScrollProgress, ScrollProgress }; diff --git a/packages/ui-native/src/components/slideshow/slideshow.tsx b/packages/ui-native/src/components/slideshow/slideshow.tsx new file mode 100644 index 00000000..eaa91b8a --- /dev/null +++ b/packages/ui-native/src/components/slideshow/slideshow.tsx @@ -0,0 +1,475 @@ +"use client"; + +import { useState } from "react"; + +import type { ReactNode, Ref } from "react"; +import { + Pressable, + ScrollView, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { + ModalLayer, + type ModalLayerCloseReason, + type ModalLayerPresentationProps, +} from "../../primitives/modal-layer"; +import { useControllableState } from "../../primitives/use-controllable-state"; +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-identified native slideshow section. */ +export type SlideshowSection = { + readonly content: ReactNode; + readonly id: string; + readonly title: string; +}; + +/** Localized labels for all native slideshow actions. */ +export type SlideshowLabels = { + readonly closeSections: string; + readonly exit: string; + readonly finish: string; + readonly markComplete: string; + readonly markIncomplete: string; + readonly next: string; + readonly openSections: string; + readonly position: (index: number, total: number) => string; + readonly previous: string; + readonly sections: string; +}; + +/** Props for a native modal slideshow with caller-owned completion state. */ +export type SlideshowProps = ModalLayerPresentationProps & { + readonly completedIds: ReadonlySet; + readonly currentSectionId?: string; + readonly defaultCurrentSectionId?: string; + readonly defaultOpen?: boolean; + readonly labels: SlideshowLabels; + readonly onComplete: () => void; + readonly onCurrentSectionIdChange?: (id: string) => void; + readonly onOpenChange?: (open: boolean) => void; + readonly onRequestClose?: (reason: ModalLayerCloseReason) => void; + readonly onToggleComplete: (id: string) => void; + readonly open?: boolean; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly safeArea?: (content: ReactNode) => ReactNode; + readonly sections: readonly SlideshowSection[]; + readonly surfaceProps?: Omit; + readonly title: string; +}; + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + content: { flex: 1 }, + footer: { + alignItems: "center", + borderTopWidth: 1, + flexDirection: "row", + justifyContent: "space-between", + }, + header: { + alignItems: "center", + borderBottomWidth: 1, + flexDirection: "row", + justifyContent: "space-between", + }, + progress: { height: 4, overflow: "hidden", width: "100%" }, + progressFill: { height: 4 }, + sectionAction: { + alignItems: "center", + flexDirection: "row", + minHeight: 44, + width: "100%", + }, + surface: { flex: 1 }, + titleBlock: { flex: 1 }, + toc: { borderBottomWidth: 1 }, +}); + +function SlideshowHeader({ + labels, + onExit, + onToggleSections, + position, + sectionsOpen, + sectionTitle, + title, +}: { + readonly labels: SlideshowLabels; + readonly onExit: () => void; + readonly onToggleSections: () => void; + readonly position: string; + readonly sectionsOpen: boolean; + readonly sectionTitle: string; + readonly title: string; +}) { + const theme = useTheme(); + return ( + + + + {sectionsOpen ? "−" : "+"} + + + + + {title} + + + {sectionTitle} + + + + {position} + + + {labels.exit} + + + ); +} +SlideshowHeader.displayName = "SlideshowHeader"; + +function SlideshowSections({ + completedIds, + currentIndex, + labels, + onNavigate, + sections, +}: { + readonly completedIds: ReadonlySet; + readonly currentIndex: number; + readonly labels: SlideshowLabels; + readonly onNavigate: (id: string) => void; + readonly sections: readonly SlideshowSection[]; +}) { + const theme = useTheme(); + return ( + + {sections.map((section, index) => { + const completed = completedIds.has(section.id); + const selected = index === currentIndex; + return ( + { + onNavigate(section.id); + }} + style={[ + styles.sectionAction, + { + backgroundColor: selected + ? theme.colors.accent + : theme.colors.background, + borderRadius: theme.radius.md, + paddingHorizontal: theme.spacing[3], + }, + ]} + > + + {section.title} + + + ); + })} + + ); +} +SlideshowSections.displayName = "SlideshowSections"; + +function SlideshowFooter({ + completed, + isFirst, + isLast, + labels, + onNext, + onPrevious, + onToggleComplete, +}: { + readonly completed: boolean; + readonly isFirst: boolean; + readonly isLast: boolean; + readonly labels: SlideshowLabels; + readonly onNext: () => void; + readonly onPrevious: () => void; + readonly onToggleComplete: () => void; +}) { + const theme = useTheme(); + return ( + + + + {labels.previous} + + + + + {completed ? labels.markIncomplete : labels.markComplete} + + + + + {isLast ? labels.finish : labels.next} + + + + ); +} +SlideshowFooter.displayName = "SlideshowFooter"; + +/** + * Full-screen native modal slideshow using the shared back, accessibility + * escape, safe-area, and reduced-motion boundaries. Completion stays caller-owned. + */ +function Slideshow({ + completedIds, + currentSectionId, + defaultCurrentSectionId, + defaultOpen = false, + labels, + onComplete, + onCurrentSectionIdChange, + onOpenChange, + onRequestClose, + onToggleComplete, + open, + reducedMotionService, + ref, + safeArea, + sections, + surfaceProps, + title, + ...presentationProps +}: SlideshowProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [visible, setVisible] = useControllableState( + open === undefined + ? { + defaultValue: defaultOpen, + mode: "uncontrolled", + onChange: onOpenChange, + } + : { mode: "controlled", onChange: onOpenChange, value: open }, + ); + const [selection, setSelection] = useControllableState( + currentSectionId === undefined + ? { + defaultValue: defaultCurrentSectionId ?? sections[0]?.id ?? "", + mode: "uncontrolled", + onChange: onCurrentSectionIdChange, + } + : { + mode: "controlled", + onChange: onCurrentSectionIdChange, + value: currentSectionId, + }, + ); + const [sectionsOpen, setSectionsOpen] = useState(false); + const foundIndex = sections.findIndex((section) => section.id === selection); + const currentIndex = foundIndex < 0 ? 0 : foundIndex; + const current = sections[currentIndex]; + const close = (reason: ModalLayerCloseReason) => { + setVisible(false); + onRequestClose?.(reason); + }; + if (!current) return null; + const completed = completedIds.has(current.id); + const progress = (currentIndex + 1) / sections.length; + + return ( + + + + + + { + close("requestClose"); + }} + onToggleSections={() => { + setSectionsOpen(!sectionsOpen); + }} + position={labels.position(currentIndex + 1, sections.length)} + sectionsOpen={sectionsOpen} + sectionTitle={current.title} + title={title} + /> + {sectionsOpen ? ( + { + setSelection(id); + setSectionsOpen(false); + }} + sections={sections} + /> + ) : null} + + {current.content} + + { + const next = sections[currentIndex + 1]; + if (next) setSelection(next.id); + else onComplete(); + }} + onPrevious={() => { + const previous = sections[currentIndex - 1]; + if (previous) setSelection(previous.id); + }} + onToggleComplete={() => { + onToggleComplete(current.id); + }} + /> + + + ); +} +Slideshow.displayName = "Slideshow"; + +export { Slideshow }; diff --git a/packages/ui-native/src/components/tree-view/tree-view.tsx b/packages/ui-native/src/components/tree-view/tree-view.tsx new file mode 100644 index 00000000..eee40ddb --- /dev/null +++ b/packages/ui-native/src/components/tree-view/tree-view.tsx @@ -0,0 +1,293 @@ +"use client"; + +import { useCallback, useMemo } from "react"; + +import type { ReactNode, Ref } from "react"; +import { + Pressable, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { toggleMultipleSelected } from "../../primitives/selection"; +import { useControllableState } from "../../primitives/use-controllable-state"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-identified node in a native hierarchy. */ +export type TreeViewNode = { + readonly disabled?: boolean; + readonly icon?: ReactNode; + readonly id: string; + readonly label: string; + readonly nodes?: readonly TreeViewNode[]; +}; + +/** Localized labels for native tree disclosure controls. */ +export type TreeViewLabels = { + readonly collapseNode: (node: TreeViewNode) => string; + readonly expandNode: (node: TreeViewNode) => string; + readonly region: string; +}; + +/** Props for controlled or uncontrolled native tree state. */ +export type TreeViewProps = Omit & { + readonly defaultExpandedIds?: readonly string[]; + readonly defaultSelectedIds?: readonly string[]; + readonly expandedIds?: readonly string[]; + readonly labels: TreeViewLabels; + readonly nodes: readonly TreeViewNode[]; + readonly onExpandedIdsChange?: (ids: readonly string[]) => void; + readonly onSelectedIdsChange?: (ids: readonly string[]) => void; + readonly ref?: Ref; + readonly selectedIds?: readonly string[]; + readonly selectionMode?: "multiple" | "single"; +}; + +type TreeRowsProps = { + readonly depth: number; + readonly expandedIds: readonly string[]; + readonly labels: TreeViewLabels; + readonly nodes: readonly TreeViewNode[]; + readonly onExpand: (id: string) => void; + readonly onSelect: (node: TreeViewNode) => void; + readonly selectedIds: readonly string[]; +}; + +const styles = StyleSheet.create({ + branch: { width: "100%" }, + disclosure: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + label: { flex: 1 }, + row: { alignItems: "center", flexDirection: "row", minHeight: 44 }, + selection: { + alignItems: "center", + flex: 1, + flexDirection: "row", + minHeight: 44, + }, +}); + +function toggledIds(ids: readonly string[], id: string): readonly string[] { + return ids.includes(id) + ? ids.filter((candidate) => candidate !== id) + : [...ids, id]; +} + +function TreeRow({ + depth, + expandedIds, + labels, + node, + onExpand, + onSelect, + selectedIds, +}: Omit & { readonly node: TreeViewNode }) { + const theme = useTheme(); + const expanded = expandedIds.includes(node.id); + const selected = selectedIds.includes(node.id); + const branch = (node.nodes?.length ?? 0) > 0; + return ( + + + {branch ? ( + { + onExpand(node.id); + }} + style={({ pressed }) => [ + styles.disclosure, + { opacity: node.disabled ? 0.5 : pressed ? 0.8 : 1 }, + ]} + > + + {expanded ? "−" : "+"} + + + ) : ( + + )} + { + onSelect(node); + }} + style={({ pressed }) => [ + styles.selection, + { + gap: theme.spacing[2], + opacity: node.disabled ? 0.5 : pressed ? 0.8 : 1, + paddingRight: theme.spacing[3], + }, + ]} + > + {node.icon} + + {node.label} + + + + {branch && expanded ? ( + + ) : null} + + ); +} +TreeRow.displayName = "TreeRow"; + +function TreeRows(props: TreeRowsProps) { + return props.nodes.map((node) => ( + + )); +} +TreeRows.displayName = "TreeRows"; + +/** + * Nested native list with separate 44-point disclosure and selection actions. + * It does not claim the browser tree keyboard pattern on touch platforms. + */ +function TreeView({ + defaultExpandedIds = [], + defaultSelectedIds = [], + expandedIds, + labels, + nodes, + onExpandedIdsChange, + onSelectedIdsChange, + ref, + selectedIds, + selectionMode = "single", + style, + ...props +}: TreeViewProps) { + const theme = useTheme(); + const [expanded, setExpanded] = useControllableState( + expandedIds === undefined + ? { + defaultValue: defaultExpandedIds, + mode: "uncontrolled", + onChange: onExpandedIdsChange, + } + : { + mode: "controlled", + onChange: onExpandedIdsChange, + value: expandedIds, + }, + ); + const [selected, setSelected] = useControllableState( + selectedIds === undefined + ? { + defaultValue: defaultSelectedIds, + mode: "uncontrolled", + onChange: onSelectedIdsChange, + } + : { + mode: "controlled", + onChange: onSelectedIdsChange, + value: selectedIds, + }, + ); + const onExpand = useCallback( + (id: string) => { + setExpanded(toggledIds(expanded, id)); + }, + [expanded, setExpanded], + ); + const onSelect = useCallback( + (node: TreeViewNode) => { + if (selectionMode === "single") { + setSelected([node.id]); + return; + } + const next = toggleMultipleSelected( + new Set(selected), + node, + (candidate) => candidate.id, + ); + setSelected([...next]); + }, + [selected, selectionMode, setSelected], + ); + const rows = useMemo( + () => ( + + ), + [expanded, labels, nodes, onExpand, onSelect, selected], + ); + + return ( + + {rows} + + ); +} +TreeView.displayName = "TreeView"; + +export { TreeView }; From 225348df7ab59c13106b236f3a54192abc98616f Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:27:35 +0200 Subject: [PATCH 03/18] pi-agent: Native animation utilities --- .../animated-text/animated-text.tsx | 294 ++++++++++++++ .../animation-utilities-native.test.tsx | 101 +++++ .../src/components/marquee/marquee.tsx | 235 +++++++++++ .../number-ticker/number-ticker.tsx | 106 +++++ .../src/components/resizable/resizable.tsx | 373 ++++++++++++++++++ 5 files changed, 1109 insertions(+) create mode 100644 packages/ui-native/src/components/animated-text/animated-text.tsx create mode 100644 packages/ui-native/src/components/animation-utilities-native.test.tsx create mode 100644 packages/ui-native/src/components/marquee/marquee.tsx create mode 100644 packages/ui-native/src/components/number-ticker/number-ticker.tsx create mode 100644 packages/ui-native/src/components/resizable/resizable.tsx diff --git a/packages/ui-native/src/components/animated-text/animated-text.tsx b/packages/ui-native/src/components/animated-text/animated-text.tsx new file mode 100644 index 00000000..364700e6 --- /dev/null +++ b/packages/ui-native/src/components/animated-text/animated-text.tsx @@ -0,0 +1,294 @@ +"use client"; + +import { + type ComponentRef, + type Ref, + useEffect, + useMemo, + useState, +} from "react"; + +import { + Animated, + StyleSheet, + Text as NativeText, + type TextProps, +} from "react-native"; + +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Native text reveal treatments. Scrambling variants use deterministic reveals. */ +export type AnimatedTextVariant = + | "decipher" + | "matrix" + | "reveal" + | "terminal" + | "typewriter"; +/** Order in which native text segments become visible. */ +export type AnimatedTextDirection = "center-out" | "end" | "random" | "start"; +/** Native text segmentation mode. */ +export type AnimatedTextSplit = "character" | "word"; + +/** Props for deterministic, reduced-motion-aware native animated text. */ +export type AnimatedTextProps = Omit & { + readonly cursor?: boolean; + readonly cursorChar?: string; + readonly direction?: AnimatedTextDirection; + readonly duration?: number; + readonly onAnimationComplete?: () => void; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref>; + readonly splitBy?: AnimatedTextSplit; + readonly stagger?: number; + readonly text: string; + readonly variant?: AnimatedTextVariant; +}; + +const styles = StyleSheet.create({ + segment: { opacity: 0 }, +}); +const glyphSegmenter = new Intl.Segmenter(undefined, { + granularity: "grapheme", +}); + +function getSegments(text: string, splitBy: AnimatedTextSplit): string[] { + if (splitBy === "word") return text.match(/\S+\s*/g) ?? []; + return Array.from(glyphSegmenter.segment(text), ({ segment }) => segment); +} + +function getDeterministicRank(segment: string, index: number): number { + return [...glyphSegmenter.segment(segment)].reduce( + (rank, { segment: glyph }) => rank * 31 + (glyph.codePointAt(0) ?? 0), + index + 17, + ); +} + +function getRevealOrder( + direction: AnimatedTextDirection, + segments: readonly string[], +): number[] { + const indices = segments.map((_, index) => index); + if (direction === "end") return indices.reverse(); + if (direction === "center-out") { + const center = (segments.length - 1) / 2; + return indices.sort((left, right) => { + const distance = Math.abs(left - center) - Math.abs(right - center); + return distance === 0 ? left - right : distance; + }); + } + if (direction === "random") { + return indices.sort((left, right) => { + const rank = + getDeterministicRank(segments[left] ?? "", left) - + getDeterministicRank(segments[right] ?? "", right); + return rank === 0 ? left - right : rank; + }); + } + return indices; +} + +function getCursorTone( + variant: AnimatedTextVariant, + colors: { readonly foreground: string; readonly primary: string }, +): string { + return variant === "matrix" || variant === "decipher" + ? colors.primary + : colors.foreground; +} + +function getRevealRanks( + direction: AnimatedTextDirection, + segments: readonly string[], +): number[] { + const ranks = segments.map(() => 0); + getRevealOrder(direction, segments).forEach((segmentIndex, rank) => { + ranks[segmentIndex] = rank; + }); + return ranks; +} + +function useTextAnimation({ + duration, + onComplete, + ranks, + reduceMotion, + stagger, + values, +}: { + readonly duration: number; + readonly onComplete?: () => void; + readonly ranks: readonly number[]; + readonly reduceMotion: boolean; + readonly stagger: number; + readonly values: readonly Animated.Value[]; +}): boolean { + const [completedValues, setCompletedValues] = useState< + readonly Animated.Value[] + >([]); + useEffect(() => { + if (reduceMotion || values.length === 0) { + values.forEach((value) => { + value.stopAnimation(); + value.setValue(1); + }); + return; + } + values.forEach((value) => { + value.setValue(0); + }); + const animation = Animated.parallel( + values.map((value, index) => + Animated.sequence([ + Animated.delay(Math.max(0, ranks[index] ?? 0) * Math.max(0, stagger)), + Animated.timing(value, { + duration: Math.max(0, duration), + toValue: 1, + useNativeDriver: true, + }), + ]), + ), + ); + animation.start(({ finished }) => { + if (finished) { + setCompletedValues(values); + onComplete?.(); + } + }); + return () => { + animation.stop(); + }; + }, [duration, onComplete, ranks, reduceMotion, stagger, values]); + return reduceMotion || completedValues === values; +} + +function AnimatedTextSegment({ + color, + segment, + value, +}: { + readonly color: string; + readonly segment: string; + readonly value: Animated.Value; +}) { + return ( + + {segment} + + ); +} +AnimatedTextSegment.displayName = "AnimatedTextSegment"; + +function AnimatedTextCursor({ + cursorChar, + tone, +}: { + readonly cursorChar: string; + readonly tone: string; +}) { + return ( + + {cursorChar} + + ); +} +AnimatedTextCursor.displayName = "AnimatedTextCursor"; + +/** + * Staggered native text reveal using RN Animated. Matrix and decipher avoid + * nondeterministic glyph churn and reveal the final text in a stable order. + */ +function AnimatedText({ + accessibilityLabel, + cursor = true, + cursorChar = "█", + direction = "start", + duration, + onAnimationComplete, + reducedMotionService, + ref, + splitBy = "word", + stagger = 70, + style, + text, + variant = "terminal", + ...props +}: AnimatedTextProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const resolvedSplit = variant === "reveal" ? splitBy : "character"; + const segments = useMemo( + () => getSegments(text, resolvedSplit), + [resolvedSplit, text], + ); + const revealRanks = useMemo( + () => getRevealRanks(direction, segments), + [direction, segments], + ); + const segmentValues = useMemo( + () => segments.map(() => new Animated.Value(1)), + [segments], + ); + const complete = useTextAnimation({ + duration: duration ?? theme.motion.duration.base, + onComplete: onAnimationComplete, + ranks: revealRanks, + reduceMotion, + stagger, + values: segmentValues, + }); + + return ( + + {segments.map((segment, index) => { + const value = segmentValues[index]; + return value ? ( + + ) : null; + })} + {cursor && !complete ? ( + + ) : null} + + ); +} +AnimatedText.displayName = "AnimatedText"; + +export { AnimatedText }; diff --git a/packages/ui-native/src/components/animation-utilities-native.test.tsx b/packages/ui-native/src/components/animation-utilities-native.test.tsx new file mode 100644 index 00000000..2073692a --- /dev/null +++ b/packages/ui-native/src/components/animation-utilities-native.test.tsx @@ -0,0 +1,101 @@ +import { fireEvent, render, screen } from "@testing-library/react-native"; +import { Text as NativeText } from "react-native"; + +import type { ReducedMotionService } from "../primitives/use-reduced-motion"; +import { ThemeProvider } from "../theme/theme-provider"; + +import { AnimatedText } from "./animated-text/animated-text"; +import { Marquee } from "./marquee/marquee"; +import { NumberTicker } from "./number-ticker/number-ticker"; +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "./resizable/resizable"; + +function createReducedMotionService(): ReducedMotionService { + return { + addEventListener: () => ({ remove: jest.fn() }), + isReduceMotionEnabled: () => new Promise(() => void 0), + }; +} + +describe("native animation utilities", () => { + it("reveals final text immediately when reduced motion is enabled", () => { + render( + + + , + ); + + expect(screen.getByLabelText("Deterministic launch")).toHaveTextContent( + "Deterministic launch", + ); + }); + + it("formats the final ticker value immediately for reduced motion", () => { + render( + , + ); + + expect(screen.getByLabelText("1,234")).toHaveTextContent("1,234"); + expect(screen.getByLabelText("1,234")).toHaveStyle({ + fontVariant: ["tabular-nums"], + }); + }); + + it("renders a reduced-motion marquee without hiding primary content", () => { + render( + + Alpha + Beta + , + ); + + expect(screen.getByTestId("marquee")).toHaveStyle({ overflow: "hidden" }); + expect(screen.getAllByText("Alpha")).toHaveLength(1); + }); + + it("resizes adjacent panels through 44-point adjustable actions", () => { + const onSizesChange = jest.fn(); + render( + + + + + , + ); + + const handle = screen.getByRole("adjustable", { + name: "Resize workspace", + }); + expect(handle).toHaveStyle({ minHeight: 44, width: 44 }); + expect(handle).toHaveAccessibilityValue({ + max: 90, + min: 10, + now: 50, + text: "50 percent", + }); + + fireEvent(handle, "accessibilityAction", { + nativeEvent: { actionName: "increment" }, + }); + + expect(onSizesChange).toHaveBeenCalledWith([55, 45]); + expect(screen.getByTestId("first-panel")).toHaveStyle({ flexGrow: 55 }); + expect(screen.getByTestId("second-panel")).toHaveStyle({ flexGrow: 45 }); + }); +}); diff --git a/packages/ui-native/src/components/marquee/marquee.tsx b/packages/ui-native/src/components/marquee/marquee.tsx new file mode 100644 index 00000000..8ed2ef13 --- /dev/null +++ b/packages/ui-native/src/components/marquee/marquee.tsx @@ -0,0 +1,235 @@ +"use client"; + +import { + Children, + type ReactNode, + type Ref, + useEffect, + useMemo, + useState, +} from "react"; + +import { + Animated, + Easing, + type LayoutChangeEvent, + StyleSheet, + View, + type ViewProps, +} from "react-native"; + +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Native marquee speed presets. */ +export type MarqueeSpeed = "fast" | "normal" | "slow"; + +/** Props for a content-sized native marquee lane. */ +export type MarqueeProps = Omit & { + readonly children: ReactNode; + readonly duration?: number; + readonly gap?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly repeat?: number; + readonly reverse?: boolean; + readonly speed?: MarqueeSpeed; + readonly vertical?: boolean; +}; + +const styles = StyleSheet.create({ + root: { overflow: "hidden", width: "100%" }, + row: { alignItems: "center", flexDirection: "row" }, + track: { flexShrink: 0 }, + vertical: { flexDirection: "column" }, +}); + +function getDuration( + duration: number | undefined, + speed: MarqueeSpeed, +): number { + if (duration !== undefined) return duration; + if (speed === "fast") return 10; + if (speed === "slow") return 32; + return 20; +} + +function MarqueeItems({ + children, + repeat, +}: { + readonly children: ReactNode; + readonly repeat: number; +}) { + const items = Children.toArray(children); + return Array.from( + { length: Math.max(1, Math.floor(repeat)) }, + (_, copyIndex) => + items.map((item, itemIndex) => ( + {item} + )), + ); +} +MarqueeItems.displayName = "MarqueeItems"; + +function useMarqueeOffset({ + duration, + gap, + laneSize, + reduceMotion, + reverse, +}: { + readonly duration: number; + readonly gap: number; + readonly laneSize: number; + readonly reduceMotion: boolean; + readonly reverse: boolean; +}): Animated.Value { + const offset = useMemo(() => new Animated.Value(0), []); + useEffect(() => { + offset.stopAnimation(); + if (reduceMotion || laneSize <= 0 || duration <= 0) { + offset.setValue(0); + return; + } + const distance = laneSize + gap; + offset.setValue(reverse ? -distance : 0); + const animation = Animated.loop( + Animated.timing(offset, { + duration: duration * 1000, + easing: Easing.linear, + toValue: reverse ? 0 : -distance, + useNativeDriver: true, + }), + ); + animation.start(); + return () => { + animation.stop(); + }; + }, [duration, gap, laneSize, offset, reduceMotion, reverse]); + return offset; +} + +function MarqueeTrack({ + children, + directionStyle, + gap, + onLaneSize, + reduceMotion, + transformStyle, + vertical, + viewportMinimum, +}: { + readonly children: ReactNode; + readonly directionStyle: ViewProps["style"]; + readonly gap: number; + readonly onLaneSize: (size: number) => void; + readonly reduceMotion: boolean; + readonly transformStyle: ViewProps["style"]; + readonly vertical: boolean; + readonly viewportMinimum: ViewProps["style"]; +}) { + return ( + + { + onLaneSize( + vertical + ? event.nativeEvent.layout.height + : event.nativeEvent.layout.width, + ); + }} + style={[directionStyle, viewportMinimum, { gap }]} + > + {children} + + {reduceMotion ? null : ( + + {children} + + )} + + ); +} +MarqueeTrack.displayName = "MarqueeTrack"; + +/** + * Continuous RN Animated content lane. Native core intentionally leaves out + * hover pausing and edge masks because it lacks those interaction primitives. + */ +function Marquee({ + children, + duration, + gap, + onLayout, + reducedMotionService, + ref, + repeat = 1, + reverse = false, + speed = "normal", + style, + vertical = false, + ...props +}: MarqueeProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [laneSize, setLaneSize] = useState(0); + const [viewportSize, setViewportSize] = useState(0); + const resolvedGap = gap ?? theme.spacing[4]; + const resolvedDuration = getDuration(duration, speed); + const offset = useMarqueeOffset({ + duration: resolvedDuration, + gap: resolvedGap, + laneSize, + reduceMotion, + reverse, + }); + const directionStyle = vertical ? styles.vertical : styles.row; + const viewportMinimum = vertical + ? { minHeight: viewportSize } + : { minWidth: viewportSize }; + const transformStyle = vertical + ? { transform: [{ translateY: offset }] } + : { transform: [{ translateX: offset }] }; + const trackItems = useMemo( + () => {children}, + [children, repeat], + ); + + const handleRootLayout = (event: LayoutChangeEvent) => { + const layout = event.nativeEvent.layout; + setViewportSize(vertical ? layout.height : layout.width); + onLayout?.(event); + }; + + return ( + + + {trackItems} + + + ); +} +Marquee.displayName = "Marquee"; + +export { Marquee }; diff --git a/packages/ui-native/src/components/number-ticker/number-ticker.tsx b/packages/ui-native/src/components/number-ticker/number-ticker.tsx new file mode 100644 index 00000000..804778f8 --- /dev/null +++ b/packages/ui-native/src/components/number-ticker/number-ticker.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { + type ComponentRef, + type Ref, + useEffect, + useMemo, + useState, +} from "react"; + +import { Animated, StyleSheet, type TextProps } from "react-native"; + +import type { ReducedMotionService } from "../../primitives/use-reduced-motion"; +import { useReducedMotion } from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for the native animated number renderer. */ +export type NumberTickerProps = Omit & { + readonly delay?: number; + readonly duration?: number; + readonly formatOptions?: Intl.NumberFormatOptions; + readonly from?: number; + readonly locale?: string; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref>; + readonly value: number; +}; + +const styles = StyleSheet.create({ + value: { fontVariant: ["tabular-nums"] }, +}); + +/** Animated native metric text with immediate reduced-motion state. */ +function NumberTicker({ + accessibilityLabel, + delay = 0, + duration = 1.2, + formatOptions, + from = 0, + locale, + reducedMotionService, + ref, + style, + value, + ...props +}: NumberTickerProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const animatedValue = useMemo(() => new Animated.Value(from), [from]); + const [currentValue, setCurrentValue] = useState(from); + const formatter = useMemo( + () => Intl.NumberFormat(locale, formatOptions), + [formatOptions, locale], + ); + + useEffect(() => { + animatedValue.stopAnimation(); + if (reduceMotion || duration <= 0) { + animatedValue.setValue(value); + setCurrentValue(value); + return; + } + + animatedValue.setValue(from); + setCurrentValue(from); + const listenerId = animatedValue.addListener(({ value: nextValue }) => { + setCurrentValue(nextValue); + }); + const animation = Animated.sequence([ + Animated.delay(Math.max(0, delay) * 1000), + Animated.timing(animatedValue, { + duration: Math.max(0, duration) * 1000, + toValue: value, + useNativeDriver: false, + }), + ]); + animation.start(); + + return () => { + animation.stop(); + animatedValue.removeListener(listenerId); + }; + }, [animatedValue, delay, duration, from, reduceMotion, value]); + + const finalLabel = formatter.format(value); + const displayedValue = reduceMotion ? value : currentValue; + return ( + + {formatter.format(displayedValue)} + + ); +} +NumberTicker.displayName = "NumberTicker"; + +export { NumberTicker }; diff --git a/packages/ui-native/src/components/resizable/resizable.tsx b/packages/ui-native/src/components/resizable/resizable.tsx new file mode 100644 index 00000000..d088adf2 --- /dev/null +++ b/packages/ui-native/src/components/resizable/resizable.tsx @@ -0,0 +1,373 @@ +"use client"; + +import { + Children, + cloneElement, + createContext, + isValidElement, + type ReactNode, + type Ref, + use, + useCallback, + useMemo, + useState, +} from "react"; + +import { + type AccessibilityActionEvent, + StyleSheet, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Native resizable panel direction. */ +export type ResizableDirection = "horizontal" | "vertical"; + +/** Props for a native resizable panel group. */ +export type ResizablePanelGroupProps = Omit & { + readonly children: ReactNode; + readonly direction?: ResizableDirection; + readonly onSizesChange?: (sizes: readonly number[]) => void; + readonly ref?: Ref; +}; + +/** Props for one native resizable panel. */ +export type ResizablePanelProps = Omit & { + readonly defaultSize?: number; + readonly maxSize?: number; + readonly minSize?: number; + readonly ref?: Ref; +}; + +/** Props for an accessible native resize control. */ +export type ResizableHandleProps = Omit< + ViewProps, + "children" | "onAccessibilityAction" | "ref" +> & { + readonly decrementLabel?: string; + readonly disabled?: boolean; + readonly incrementLabel?: string; + readonly ref?: Ref; + readonly step?: number; + readonly withHandle?: boolean; +}; + +type PanelConfig = { + readonly defaultSize: number; + readonly maxSize: number; + readonly minSize: number; +}; + +type ResizableContextValue = { + readonly adjust: (handleIndex: number, amount: number) => void; + readonly configs: readonly PanelConfig[]; + readonly direction: ResizableDirection; + readonly sizes: readonly number[]; +}; + +type InternalResizablePanelProps = ResizablePanelProps & { + readonly panelIndex?: number; +}; + +type InternalResizableHandleProps = ResizableHandleProps & { + readonly handleIndex?: number; +}; + +const ResizableContext = createContext(null); + +const styles = StyleSheet.create({ + dividerHorizontal: { height: "100%", width: 1 }, + dividerVertical: { height: 1, width: "100%" }, + gripHorizontal: { borderWidth: 1, height: 20, width: 12 }, + gripVertical: { borderWidth: 1, height: 12, width: 20 }, + handle: { alignItems: "center", justifyContent: "center" }, + handleHorizontal: { alignSelf: "stretch", minHeight: 44, width: 44 }, + handleVertical: { height: 44, minWidth: 44, width: "100%" }, + horizontal: { flexDirection: "row" }, + panel: { flexBasis: 0, flexShrink: 1, overflow: "hidden" }, + root: { alignItems: "stretch", height: "100%", width: "100%" }, + vertical: { flexDirection: "column" }, +}); + +function clamp(value: number, min: number, max: number): number { + return Math.min(Math.max(value, min), max); +} + +function getPanelConfigs(children: ReactNode): PanelConfig[] { + return Children.toArray(children).reduce((configs, child) => { + if ( + isValidElement(child) && + child.type === ResizablePanel + ) { + const minSize = clamp(child.props.minSize ?? 10, 0, 100); + const maxSize = clamp(child.props.maxSize ?? 90, minSize, 100); + configs.push({ + defaultSize: clamp(child.props.defaultSize ?? 50, minSize, maxSize), + maxSize, + minSize, + }); + } + return configs; + }, []); +} + +function normalizeSizes(configs: readonly PanelConfig[]): number[] { + if (configs.length === 0) return []; + const total = configs.reduce((sum, config) => sum + config.defaultSize, 0); + if (total <= 0) return configs.map(() => 100 / configs.length); + return configs.map((config) => (config.defaultSize / total) * 100); +} + +function resizePanels({ + amount, + configs, + handleIndex, + sizes, +}: { + readonly amount: number; + readonly configs: readonly PanelConfig[]; + readonly handleIndex: number; + readonly sizes: readonly number[]; +}): readonly number[] { + const afterIndex = handleIndex + 1; + const beforeConfig = configs[handleIndex]; + const afterConfig = configs[afterIndex]; + const beforeSize = sizes[handleIndex]; + const afterSize = sizes[afterIndex]; + if ( + !beforeConfig || + !afterConfig || + beforeSize === undefined || + afterSize === undefined + ) { + return sizes; + } + const increaseLimit = Math.min( + beforeConfig.maxSize - beforeSize, + afterSize - afterConfig.minSize, + ); + const decreaseLimit = Math.min( + beforeSize - beforeConfig.minSize, + afterConfig.maxSize - afterSize, + ); + const resolvedAmount = clamp(amount, -decreaseLimit, increaseLimit); + if (resolvedAmount === 0) return sizes; + return sizes.map((size, index) => { + if (index === handleIndex) return size + resolvedAmount; + if (index === afterIndex) return size - resolvedAmount; + return size; + }); +} + +function indexResizableChildren(children: ReactNode): ReactNode { + const childArray = Children.toArray(children); + return childArray.map((child, childIndex) => { + const preceding = childArray.slice(0, childIndex); + if ( + isValidElement(child) && + child.type === ResizablePanel + ) { + const panelIndex = preceding.filter( + (candidate) => + isValidElement(candidate) && + candidate.type === ResizablePanel, + ).length; + return cloneElement(child, { panelIndex }); + } + if ( + isValidElement(child) && + child.type === ResizableHandle + ) { + const handleIndex = preceding.filter( + (candidate) => + isValidElement(candidate) && + candidate.type === ResizableHandle, + ).length; + return cloneElement(child, { handleIndex }); + } + return child; + }); +} + +/** + * Native panel layout coordinated by accessible resize actions. Pointer and + * touch dragging are intentionally omitted rather than approximating DOM drag. + */ +function ResizablePanelGroup({ + children, + direction = "horizontal", + onSizesChange, + ref, + style, + ...props +}: ResizablePanelGroupProps) { + const configs = useMemo(() => getPanelConfigs(children), [children]); + const [sizes, setSizes] = useState(() => + normalizeSizes(configs), + ); + const adjust = useCallback( + (handleIndex: number, amount: number) => { + const nextSizes = resizePanels({ amount, configs, handleIndex, sizes }); + if (nextSizes === sizes) return; + setSizes(nextSizes); + onSizesChange?.(nextSizes); + }, + [configs, onSizesChange, sizes], + ); + const context = useMemo( + () => ({ adjust, configs, direction, sizes }), + [adjust, configs, direction, sizes], + ); + const indexedChildren = useMemo( + () => indexResizableChildren(children), + [children], + ); + + return ( + + + {indexedChildren} + + + ); +} +ResizablePanelGroup.displayName = "ResizablePanelGroup"; + +/** Flexible content region inside a native ResizablePanelGroup. */ +function ResizablePanel({ + defaultSize = 50, + maxSize = 100, + minSize = 0, + panelIndex = 0, + ref, + style, + ...props +}: InternalResizablePanelProps) { + const context = use(ResizableContext); + const size = + context?.sizes[panelIndex] ?? clamp(defaultSize, minSize, maxSize); + return ( + + ); +} +ResizablePanel.displayName = "ResizablePanel"; + +function ResizeIndicator({ + horizontal, + withHandle, +}: { + readonly horizontal: boolean; + readonly withHandle: boolean; +}) { + const theme = useTheme(); + return ( + <> + + {withHandle ? ( + + ) : null} + + ); +} +ResizeIndicator.displayName = "ResizeIndicator"; + +/** 44-point adjustable action between adjacent native panels. */ +function ResizableHandle({ + accessibilityLabel = "Resize panels", + accessibilityState, + decrementLabel = "Decrease previous panel", + disabled = false, + handleIndex = 0, + incrementLabel = "Increase previous panel", + ref, + step = 5, + style, + withHandle = false, + ...props +}: InternalResizableHandleProps) { + const context = use(ResizableContext); + const direction = context?.direction ?? "horizontal"; + const config = context?.configs[handleIndex]; + const size = context?.sizes[handleIndex]; + const isDisabled = + disabled || !context || config === undefined || size === undefined; + const change = (amount: number) => { + if (!isDisabled) context.adjust(handleIndex, amount); + }; + const handleAccessibilityAction = (event: AccessibilityActionEvent) => { + const resolvedStep = step > 0 ? step : 1; + if (event.nativeEvent.actionName === "increment") change(resolvedStep); + if (event.nativeEvent.actionName === "decrement") change(-resolvedStep); + }; + const horizontal = direction === "horizontal"; + + return ( + + + + ); +} +ResizableHandle.displayName = "ResizableHandle"; + +export { ResizableHandle, ResizablePanel, ResizablePanelGroup }; From a97608f9d5494fb6977c8e69755e48439df11335 Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:32:29 +0200 Subject: [PATCH 04/18] pi-agent: Native content utilities --- .../components/blur-reveal/blur-reveal.tsx | 71 +++++ .../src/components/code-block/code-block.tsx | 228 +++++++++++++ .../document-sibling-nav.tsx | 218 +++++++++++++ .../components/motion-content-native.test.tsx | 205 ++++++++++++ .../components/reveal-text/reveal-text.tsx | 118 +++++++ .../scramble-text/scramble-text.tsx | 133 ++++++++ .../share-section/share-section.tsx | 152 +++++++++ .../components/shimmer-text/shimmer-text.tsx | 91 ++++++ .../spinning-text/spinning-text.tsx | 142 +++++++++ .../src/components/terminal/terminal.tsx | 301 ++++++++++++++++++ .../components/text-animate/text-animate.tsx | 147 +++++++++ .../components/text-reveal/text-reveal.tsx | 81 +++++ .../components/text-shimmer/text-shimmer.tsx | 91 ++++++ .../src/components/typewriter/typewriter.tsx | 100 ++++++ 14 files changed, 2078 insertions(+) create mode 100644 packages/ui-native/src/components/blur-reveal/blur-reveal.tsx create mode 100644 packages/ui-native/src/components/code-block/code-block.tsx create mode 100644 packages/ui-native/src/components/document-sibling-nav/document-sibling-nav.tsx create mode 100644 packages/ui-native/src/components/motion-content-native.test.tsx create mode 100644 packages/ui-native/src/components/reveal-text/reveal-text.tsx create mode 100644 packages/ui-native/src/components/scramble-text/scramble-text.tsx create mode 100644 packages/ui-native/src/components/share-section/share-section.tsx create mode 100644 packages/ui-native/src/components/shimmer-text/shimmer-text.tsx create mode 100644 packages/ui-native/src/components/spinning-text/spinning-text.tsx create mode 100644 packages/ui-native/src/components/terminal/terminal.tsx create mode 100644 packages/ui-native/src/components/text-animate/text-animate.tsx create mode 100644 packages/ui-native/src/components/text-reveal/text-reveal.tsx create mode 100644 packages/ui-native/src/components/text-shimmer/text-shimmer.tsx create mode 100644 packages/ui-native/src/components/typewriter/typewriter.tsx diff --git a/packages/ui-native/src/components/blur-reveal/blur-reveal.tsx b/packages/ui-native/src/components/blur-reveal/blur-reveal.tsx new file mode 100644 index 00000000..b4ace0ac --- /dev/null +++ b/packages/ui-native/src/components/blur-reveal/blur-reveal.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { type ReactNode, type Ref, useEffect, useState } from "react"; + +import { Animated, Easing, type View, type ViewProps } from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for a native opacity reveal. React Native core has no portable view blur. */ +export type BlurRevealProps = Omit & { + readonly children: ReactNode; + readonly delay?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly visible?: boolean; +}; + +/** Reveals native content with the portable opacity from the web effect. */ +function BlurReveal({ + children, + delay = 0, + reducedMotionService, + ref, + style, + visible = true, + ...props +}: BlurRevealProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [progress, setProgress] = useState( + () => new Animated.Value(visible ? 1 : 0), + ); + void setProgress; + + useEffect(() => { + if (reduceMotion) { + progress.setValue(visible ? 1 : 0); + return; + } + const animation = Animated.timing(progress, { + delay: visible ? Math.max(0, delay) : 0, + duration: theme.motion.duration.slow, + easing: Easing.out(Easing.cubic), + toValue: visible ? 1 : 0, + useNativeDriver: true, + }); + animation.start(); + return () => { + animation.stop(); + }; + }, [delay, progress, reduceMotion, theme.motion.duration.slow, visible]); + + return ( + + {children} + + ); +} +BlurReveal.displayName = "BlurReveal"; + +export { BlurReveal }; diff --git a/packages/ui-native/src/components/code-block/code-block.tsx b/packages/ui-native/src/components/code-block/code-block.tsx new file mode 100644 index 00000000..66835e84 --- /dev/null +++ b/packages/ui-native/src/components/code-block/code-block.tsx @@ -0,0 +1,228 @@ +"use client"; + +import { type ReactNode, type Ref, useState } from "react"; + +import { + Pressable, + ScrollView, + StyleSheet, + Text as NativeText, + View, + type ViewProps, +} from "react-native"; + +import type { ClipboardService } from "../../primitives/platform-services"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-localized labels for optional code copying. */ +export type CodeBlockCopyLabels = { + readonly copied: string; + readonly copy: string; + readonly unavailable: string; +}; + +/** Context passed to an optional host syntax renderer. */ +export type CodeBlockRenderContext = { + readonly code: string; + readonly language?: string; +}; + +/** Props for a dependency-free native code surface. */ +export type CodeBlockProps = Omit & { + readonly clipboard?: ClipboardService; + readonly code: string; + readonly copyLabels?: CodeBlockCopyLabels; + readonly language?: string; + readonly onCopyError?: (error: unknown) => void; + readonly onCopySuccess?: () => void; + readonly ref?: Ref; + readonly renderCode?: (context: CodeBlockRenderContext) => ReactNode; + readonly showLanguage?: boolean; +}; + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + header: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + minHeight: 44, + }, + root: { borderWidth: 1, overflow: "hidden", width: "100%" }, +}); + +type HeaderProps = { + readonly available: boolean; + readonly label?: string; + readonly language?: string; + readonly onCopy: () => void; + readonly showLanguage: boolean; +}; + +function CodeBlockHeader({ + available, + label, + language, + onCopy, + showLanguage, +}: HeaderProps) { + const theme = useTheme(); + return ( + + {showLanguage && language ? ( + + {language} + + ) : ( + + )} + {label ? ( + [ + styles.action, + { + backgroundColor: pressed + ? theme.colors.accent + : theme.colors.muted, + borderRadius: theme.radius.md, + opacity: available ? 1 : 0.5, + paddingHorizontal: theme.spacing[3], + }, + ]} + > + + {label} + + + ) : null} + + ); +} +CodeBlockHeader.displayName = "CodeBlockHeader"; + +function CodeContent({ + code, + language, + renderCode, +}: { + readonly code: string; + readonly language?: string; + readonly renderCode?: (context: CodeBlockRenderContext) => ReactNode; +}) { + const theme = useTheme(); + return ( + + {renderCode ? ( + renderCode({ code, language }) + ) : ( + + {code} + + )} + + ); +} +CodeContent.displayName = "CodeContent"; + +/** + * Renders selectable plain code. Hosts inject syntax rendering and clipboard + * support explicitly. + */ +function CodeBlock({ + clipboard, + code, + copyLabels, + language, + onCopyError, + onCopySuccess, + ref, + renderCode, + showLanguage = false, + style, + ...props +}: CodeBlockProps) { + const theme = useTheme(); + const [copiedCode, setCopiedCode] = useState(); + const copyAvailable = clipboard !== undefined; + const copied = copiedCode === code; + const copyLabel = copied + ? copyLabels?.copied + : copyAvailable + ? copyLabels?.copy + : copyLabels?.unavailable; + const copy = async () => { + if (!clipboard) return; + try { + await clipboard.setText(code); + setCopiedCode(code); + onCopySuccess?.(); + } catch (error: unknown) { + onCopyError?.(error); + } + }; + + return ( + + {showLanguage || copyLabels ? ( + void copy()} + showLanguage={showLanguage} + /> + ) : null} + + + ); +} +CodeBlock.displayName = "CodeBlock"; + +export { CodeBlock }; diff --git a/packages/ui-native/src/components/document-sibling-nav/document-sibling-nav.tsx b/packages/ui-native/src/components/document-sibling-nav/document-sibling-nav.tsx new file mode 100644 index 00000000..299b42b7 --- /dev/null +++ b/packages/ui-native/src/components/document-sibling-nav/document-sibling-nav.tsx @@ -0,0 +1,218 @@ +"use client"; + +import type { Ref } from "react"; +import { + Pressable, + StyleSheet, + Text as NativeText, + View, + type ViewProps, +} from "react-native"; + +import { + defaultLinkingService, + type LinkingService, + type OpenUrlResult, +} from "../../primitives/platform-services"; +import { useTheme } from "../../theme/theme-provider"; + +/** Native sibling-navigation presentation. */ +export type DocumentSiblingNavVariant = "compact" | "with-meta" | "with-title"; + +/** A native document destination. */ +export type DocumentSiblingNavLink = { + readonly href: string; + readonly meta?: string; + readonly title: string; +}; + +/** Caller-localized native navigation labels. */ +export type DocumentSiblingNavLabels = { + readonly navigation: string; + readonly next: string; + readonly previous: string; +}; + +/** Props for native previous and next document links. */ +export type DocumentSiblingNavProps = Omit & { + readonly labels: DocumentSiblingNavLabels; + readonly linking?: LinkingService; + readonly next?: DocumentSiblingNavLink; + readonly onOpenError?: (error: unknown, link: DocumentSiblingNavLink) => void; + readonly onOpenResult?: ( + result: OpenUrlResult, + link: DocumentSiblingNavLink, + ) => void; + readonly previous?: DocumentSiblingNavLink; + readonly ref?: Ref; + readonly variant?: DocumentSiblingNavVariant; +}; + +const styles = StyleSheet.create({ + item: { flex: 1, justifyContent: "center", minHeight: 44 }, + next: { alignItems: "flex-end" }, + previous: { alignItems: "flex-start" }, + root: { flexDirection: "row", width: "100%" }, +}); + +function buildLabel( + caption: string, + link: DocumentSiblingNavLink, + variant: DocumentSiblingNavVariant, +) { + return variant === "compact" ? caption : `${caption}: ${link.title}`; +} + +function SiblingText({ + caption, + link, + variant, +}: { + readonly caption: string; + readonly link: DocumentSiblingNavLink; + readonly variant: DocumentSiblingNavVariant; +}) { + const theme = useTheme(); + return ( + <> + + {caption} + + {variant === "compact" ? null : ( + + {link.title} + + )} + {variant === "with-meta" && link.meta ? ( + + {link.meta} + + ) : null} + + ); +} +SiblingText.displayName = "SiblingText"; + +type SiblingLinkProps = { + readonly caption: string; + readonly link: DocumentSiblingNavLink; + readonly linking: LinkingService; + readonly onOpenError?: (error: unknown, link: DocumentSiblingNavLink) => void; + readonly onOpenResult?: ( + result: OpenUrlResult, + link: DocumentSiblingNavLink, + ) => void; + readonly side: "next" | "previous"; + readonly variant: DocumentSiblingNavVariant; +}; + +function SiblingLink({ + caption, + link, + linking, + onOpenError, + onOpenResult, + side, + variant, +}: SiblingLinkProps) { + const theme = useTheme(); + return ( + { + void linking.openUrl(link.href).then( + (result) => onOpenResult?.(result, link), + (error: unknown) => onOpenError?.(error, link), + ); + }} + style={({ pressed }) => [ + styles.item, + side === "next" ? styles.next : styles.previous, + { + backgroundColor: pressed + ? theme.colors.accent + : theme.colors.background, + borderColor: theme.colors.border, + borderRadius: theme.radius.md, + borderWidth: 1, + gap: theme.spacing[1], + padding: theme.spacing[4], + }, + ]} + > + + + ); +} +SiblingLink.displayName = "SiblingLink"; + +/** Opens sibling document URLs through an injectable native linking service. */ +function DocumentSiblingNav({ + labels, + linking = defaultLinkingService, + next, + onOpenError, + onOpenResult, + previous, + ref, + style, + variant = "with-title", + ...props +}: DocumentSiblingNavProps) { + const theme = useTheme(); + if (!previous && !next) return null; + return ( + + {previous ? ( + + ) : ( + + )} + {next ? ( + + ) : null} + + ); +} +DocumentSiblingNav.displayName = "DocumentSiblingNav"; + +export { DocumentSiblingNav }; diff --git a/packages/ui-native/src/components/motion-content-native.test.tsx b/packages/ui-native/src/components/motion-content-native.test.tsx new file mode 100644 index 00000000..7815750a --- /dev/null +++ b/packages/ui-native/src/components/motion-content-native.test.tsx @@ -0,0 +1,205 @@ +import { + fireEvent, + render, + screen, + waitFor, +} from "@testing-library/react-native"; +import { Text as NativeText, View } from "react-native"; + +import { BlurReveal } from "./blur-reveal/blur-reveal"; +import { CodeBlock } from "./code-block/code-block"; +import { DocumentSiblingNav } from "./document-sibling-nav/document-sibling-nav"; +import { RevealText } from "./reveal-text/reveal-text"; +import { ScrambleText } from "./scramble-text/scramble-text"; +import { ShareSection } from "./share-section/share-section"; +import { ShimmerText } from "./shimmer-text/shimmer-text"; +import { SpinningText } from "./spinning-text/spinning-text"; +import { Terminal } from "./terminal/terminal"; +import { TextAnimate } from "./text-animate/text-animate"; +import { TextReveal } from "./text-reveal/text-reveal"; +import { TextShimmer } from "./text-shimmer/text-shimmer"; +import { Typewriter } from "./typewriter/typewriter"; + +const reducedMotionService = { + addEventListener( + eventName: "reduceMotionChanged", + listener: (enabled: boolean) => void, + ) { + void eventName; + void listener; + return { + remove() { + return; + }, + }; + }, + async isReduceMotionEnabled() { + return true; + }, +}; + +describe("native motion and content utilities", () => { + it("renders complete accessible text when motion is reduced", async () => { + render( + + + Blur fallback + + + Controlled reveal + + + + Shimmer + + + Native ring + + + Animated words + + + Readable words + + + Text shimmer + + + , + ); + + await waitFor(() => { + expect(screen.getByText("SCRAMBLE")).toBeOnTheScreen(); + expect(screen.getByText("Typed text")).toBeOnTheScreen(); + }); + expect(screen.getByLabelText("Native ring")).toBeOnTheScreen(); + expect(screen.getByLabelText("Animated words")).toBeOnTheScreen(); + expect(screen.getByLabelText("Readable words")).toBeOnTheScreen(); + }); + + it("keeps code plain unless a renderer is injected and uses an explicit clipboard", async () => { + const setText = jest.fn(async (): Promise => { + await Promise.resolve(); + }); + render( + "", setText }} + code="const native = true;" + copyLabels={{ + copied: "Code copied", + copy: "Copy native code", + unavailable: "Copy unavailable", + }} + language="typescript" + showLanguage + />, + ); + + expect(screen.getByText("const native = true;")).toHaveProp( + "selectable", + true, + ); + fireEvent.press(screen.getByRole("button", { name: "Copy native code" })); + await waitFor(() => { + expect(setText).toHaveBeenCalledWith("const native = true;"); + expect( + screen.getByRole("button", { name: "Code copied" }), + ).toBeOnTheScreen(); + }); + }); + + it("opens document links and the native share sheet through injected services", async () => { + const openUrl = jest.fn( + async (): Promise<{ readonly status: "opened" }> => ({ + status: "opened", + }), + ); + const share = jest.fn( + async (): Promise<{ readonly status: "shared" }> => ({ + status: "shared", + }), + ); + render( + + + + , + ); + + fireEvent.press( + screen.getByRole("link", { name: "Next article: Native follow-up" }), + ); + fireEvent.press(screen.getByRole("button", { name: "Share release" })); + + await waitFor(() => { + expect(openUrl).toHaveBeenCalledWith("https://example.com/next"); + expect(share).toHaveBeenCalledWith( + { message: "Native release", url: "https://example.com" }, + undefined, + ); + }); + }); + + it("copies only terminal command lines and exposes unavailable copy truthfully", async () => { + const setText = jest.fn(async (): Promise => { + await Promise.resolve(); + }); + const { rerender } = render( + "", setText }} + copyLabels={{ + copied: "Commands copied", + copy: "Copy commands", + unavailable: "Copy unavailable", + }} + lines={[ + { content: "pnpm test", type: "command" }, + { content: "Tests passed", type: "output" }, + ]} + title="Test terminal" + />, + ); + + fireEvent.press(screen.getByRole("button", { name: "Copy commands" })); + await waitFor(() => { + expect(setText).toHaveBeenCalledWith("pnpm test"); + }); + + rerender( + , + ); + expect( + screen.getByRole("button", { name: "Copy unavailable" }), + ).toBeDisabled(); + }); +}); diff --git a/packages/ui-native/src/components/reveal-text/reveal-text.tsx b/packages/ui-native/src/components/reveal-text/reveal-text.tsx new file mode 100644 index 00000000..22b10679 --- /dev/null +++ b/packages/ui-native/src/components/reveal-text/reveal-text.tsx @@ -0,0 +1,118 @@ +"use client"; + +import { type ReactNode, type Ref, useEffect, useState } from "react"; + +import { + Animated, + Easing, + StyleSheet, + type View, + type ViewProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Native slide-in origin. */ +export type RevealDirection = "down" | "left" | "right" | "up"; + +/** Props for an explicitly controlled native reveal. */ +export type RevealTextProps = Omit & { + readonly children: ReactNode; + readonly delay?: number; + readonly direction?: RevealDirection; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly visible?: boolean; +}; + +const styles = StyleSheet.create({ root: { overflow: "hidden" } }); + +function directionOffset(direction: RevealDirection, distance: number) { + switch (direction) { + case "down": + return { x: 0, y: -distance }; + case "left": + return { x: distance, y: 0 }; + case "right": + return { x: -distance, y: 0 }; + case "up": + return { x: 0, y: distance }; + } +} + +/** Slides content when `visible` changes; viewport detection stays with the host. */ +function RevealText({ + children, + delay = 0, + direction = "up", + reducedMotionService, + ref, + style, + visible = true, + ...props +}: RevealTextProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [progress, setProgress] = useState( + () => new Animated.Value(visible ? 1 : 0), + ); + void setProgress; + const offset = directionOffset(direction, theme.spacing[2]); + + useEffect(() => { + if (reduceMotion) { + progress.setValue(visible ? 1 : 0); + return; + } + const animation = Animated.timing(progress, { + delay: visible ? Math.max(0, delay) : 0, + duration: theme.motion.duration.slow, + easing: Easing.out(Easing.cubic), + toValue: visible ? 1 : 0, + useNativeDriver: true, + }); + animation.start(); + return () => { + animation.stop(); + }; + }, [delay, progress, reduceMotion, theme.motion.duration.slow, visible]); + + return ( + + {children} + + ); +} +RevealText.displayName = "RevealText"; + +export { RevealText }; diff --git a/packages/ui-native/src/components/scramble-text/scramble-text.tsx b/packages/ui-native/src/components/scramble-text/scramble-text.tsx new file mode 100644 index 00000000..71d36085 --- /dev/null +++ b/packages/ui-native/src/components/scramble-text/scramble-text.tsx @@ -0,0 +1,133 @@ +"use client"; + +import { type Ref, useEffect, useState } from "react"; + +import { + Text as NativeText, + type Text as NativeTextInstance, + type TextProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for deterministic native scrambled text. */ +export type ScrambleTextProps = Omit & { + readonly duration?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly scrambleCharacters?: string; + readonly text: string; +}; + +const defaultPool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +function splitCharacters(value: string): readonly string[] { + return value.match(/[\s\S]/gu) ?? []; +} + +function scramble(text: string, revealed: number, pool: string): string { + if (pool.length === 0) return text; + return splitCharacters(text) + .map((character, index) => { + if (index < revealed || character.trim().length === 0) return character; + return pool.charAt((index * 17 + revealed * 13) % pool.length); + }) + .join(""); +} + +function useRevealCount({ + duration, + pool, + reduceMotion, + text, +}: { + readonly duration: number; + readonly pool: string; + readonly reduceMotion: boolean; + readonly text: string; +}): number { + const [state, setState] = useState({ + pool, + reduceMotion, + revealed: text.length, + text, + }); + if ( + state.pool !== pool || + state.reduceMotion !== reduceMotion || + state.text !== text + ) { + setState({ + pool, + reduceMotion, + revealed: reduceMotion || pool.length === 0 ? text.length : 0, + text, + }); + } + useEffect(() => { + if (reduceMotion || text.length === 0 || pool.length === 0) return; + const timer = setInterval( + () => { + setState((current) => + current.revealed >= text.length + ? current + : { ...current, revealed: current.revealed + 1 }, + ); + }, + Math.max(1, Math.floor(duration / text.length)), + ); + return () => { + clearInterval(timer); + }; + }, [duration, pool, reduceMotion, text]); + const current = + state.pool === pool && + state.reduceMotion === reduceMotion && + state.text === text; + return current ? state.revealed : reduceMotion ? text.length : 0; +} + +/** Resolves a deterministic glyph sequence without randomness or browser APIs. */ +function ScrambleText({ + accessibilityLabel, + duration = 1200, + reducedMotionService, + ref, + scrambleCharacters = defaultPool, + style, + text, + ...props +}: ScrambleTextProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const revealed = useRevealCount({ + duration, + pool: scrambleCharacters, + reduceMotion, + text, + }); + return ( + + {reduceMotion ? text : scramble(text, revealed, scrambleCharacters)} + + ); +} +ScrambleText.displayName = "ScrambleText"; + +export { ScrambleText }; diff --git a/packages/ui-native/src/components/share-section/share-section.tsx b/packages/ui-native/src/components/share-section/share-section.tsx new file mode 100644 index 00000000..0c67f7e4 --- /dev/null +++ b/packages/ui-native/src/components/share-section/share-section.tsx @@ -0,0 +1,152 @@ +"use client"; + +import { type Ref, useState } from "react"; + +import type { ShareContent, ShareOptions } from "react-native"; +import { + Pressable, + StyleSheet, + Text as NativeText, + View, + type ViewProps, +} from "react-native"; + +import { + defaultShareService, + type ShareResult, + type ShareService, +} from "../../primitives/platform-services"; +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-localized labels for native sharing. */ +export type ShareSectionLabels = { + readonly share: string; + readonly unavailable: string; +}; + +/** Props for a native share-sheet section. */ +export type ShareSectionProps = Omit & { + readonly content: ShareContent; + readonly labels: ShareSectionLabels; + readonly onShareError?: (error: unknown) => void; + readonly onShareResult?: (result: ShareResult) => void; + readonly options?: ShareOptions; + readonly ref?: Ref; + readonly shareService?: null | ShareService; + readonly title: string; +}; + +const styles = StyleSheet.create({ + action: { alignItems: "center", justifyContent: "center", minHeight: 44 }, + root: { borderTopWidth: 1, width: "100%" }, +}); + +function ShareAction({ + available, + label, + onPress, + sharing, +}: { + readonly available: boolean; + readonly label: string; + readonly onPress: () => void; + readonly sharing: boolean; +}) { + const theme = useTheme(); + return ( + [ + styles.action, + { + alignSelf: "flex-start", + backgroundColor: pressed + ? theme.colors.accent + : theme.colors.secondary, + borderRadius: theme.radius.md, + opacity: available ? 1 : 0.5, + paddingHorizontal: theme.spacing[4], + }, + ]} + > + + {label} + + + ); +} +ShareAction.displayName = "ShareAction"; + +/** Uses the native share sheet rather than fabricating browser social intents. */ +function ShareSection({ + content, + labels, + onShareError, + onShareResult, + options, + ref, + shareService, + style, + title, + ...props +}: ShareSectionProps) { + const theme = useTheme(); + const [sharing, setSharing] = useState(false); + const service = + shareService === undefined ? defaultShareService : shareService; + const available = service !== null; + const actionLabel = available ? labels.share : labels.unavailable; + const share = async () => { + if (!service || sharing) return; + setSharing(true); + try { + const result = await service.share(content, options); + onShareResult?.(result); + } catch (error: unknown) { + onShareError?.(error); + } finally { + setSharing(false); + } + }; + return ( + + + {title} + + void share()} + sharing={sharing} + /> + + ); +} +ShareSection.displayName = "ShareSection"; + +export { ShareSection }; diff --git a/packages/ui-native/src/components/shimmer-text/shimmer-text.tsx b/packages/ui-native/src/components/shimmer-text/shimmer-text.tsx new file mode 100644 index 00000000..ae0f793a --- /dev/null +++ b/packages/ui-native/src/components/shimmer-text/shimmer-text.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { type Ref, useEffect, useState } from "react"; + +import { + Animated, + Easing, + type Text as NativeTextInstance, + type TextProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for a native semantic-color shimmer fallback. */ +export type ShimmerTextProps = Omit & { + readonly children: string; + readonly duration?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; +}; + +/** Cycles semantic text color; a sweeping gradient requires an injected renderer. */ +function ShimmerText({ + children, + duration = 3000, + reducedMotionService, + ref, + style, + ...props +}: ShimmerTextProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [progress, setProgress] = useState(() => new Animated.Value(0)); + void setProgress; + + useEffect(() => { + if (reduceMotion) { + progress.setValue(0); + return; + } + const animation = Animated.loop( + Animated.sequence([ + Animated.timing(progress, { + duration: Math.max(theme.motion.duration.base, duration / 2), + easing: Easing.inOut(Easing.cubic), + toValue: 1, + useNativeDriver: false, + }), + Animated.timing(progress, { + duration: Math.max(theme.motion.duration.base, duration / 2), + easing: Easing.inOut(Easing.cubic), + toValue: 0, + useNativeDriver: false, + }), + ]), + ); + animation.start(); + return () => { + animation.stop(); + }; + }, [duration, progress, reduceMotion, theme.motion.duration.base]); + + return ( + + {children} + + ); +} +ShimmerText.displayName = "ShimmerText"; + +export { ShimmerText }; diff --git a/packages/ui-native/src/components/spinning-text/spinning-text.tsx b/packages/ui-native/src/components/spinning-text/spinning-text.tsx new file mode 100644 index 00000000..87b22bf7 --- /dev/null +++ b/packages/ui-native/src/components/spinning-text/spinning-text.tsx @@ -0,0 +1,142 @@ +"use client"; + +import { type Ref, useEffect, useState } from "react"; + +import { + Animated, + Easing, + StyleSheet, + Text as NativeText, + type View, + type ViewProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for native text arranged around a rotating ring. */ +export type SpinningTextProps = Omit & { + readonly active?: boolean; + readonly children: string; + readonly duration?: number; + readonly radius?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly reverse?: boolean; +}; + +const styles = StyleSheet.create({ + character: { position: "absolute" }, + root: { position: "relative" }, +}); + +function splitCharacters(value: string): readonly string[] { + return value.match(/[\s\S]/gu) ?? []; +} + +function RingCharacters({ + characters, + radius, +}: { + readonly characters: readonly string[]; + readonly radius: number; +}) { + const theme = useTheme(); + return characters.map((character, index) => { + const angle = (360 / characters.length) * index; + const radians = (angle * Math.PI) / 180; + return ( + + {character} + + ); + }); +} +RingCharacters.displayName = "RingCharacters"; + +/** Rotates a semantic-color character ring and stops for reduced motion. */ +function SpinningText({ + accessibilityLabel, + active = true, + children, + duration = 20_000, + radius = 80, + reducedMotionService, + ref, + reverse = false, + style, + ...props +}: SpinningTextProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [rotation, setRotation] = useState(() => new Animated.Value(0)); + void setRotation; + const characters = splitCharacters(children); + const safeRadius = Math.max(theme.spacing[4], radius); + useEffect(() => { + rotation.setValue(0); + if (!active || reduceMotion) return; + const animation = Animated.loop( + Animated.timing(rotation, { + duration: Math.max(theme.motion.duration.slow, duration), + easing: Easing.linear, + toValue: 1, + useNativeDriver: true, + }), + ); + animation.start(); + return () => { + animation.stop(); + }; + }, [active, duration, reduceMotion, rotation, theme.motion.duration.slow]); + return ( + + + + ); +} +SpinningText.displayName = "SpinningText"; + +export { SpinningText }; diff --git a/packages/ui-native/src/components/terminal/terminal.tsx b/packages/ui-native/src/components/terminal/terminal.tsx new file mode 100644 index 00000000..403f1492 --- /dev/null +++ b/packages/ui-native/src/components/terminal/terminal.tsx @@ -0,0 +1,301 @@ +"use client"; + +import { type Ref, useMemo, useState } from "react"; + +import { + Pressable, + ScrollView, + StyleSheet, + Text as NativeText, + View, + type ViewProps, +} from "react-native"; + +import type { ClipboardService } from "../../primitives/platform-services"; +import { useTheme } from "../../theme/theme-provider"; + +/** A native terminal line. */ +export type TerminalLine = { + readonly content: string; + readonly type: "command" | "comment" | "output"; +}; + +/** Caller-localized labels for optional terminal copying. */ +export type TerminalCopyLabels = { + readonly copied: string; + readonly copy: string; + readonly unavailable: string; +}; + +/** Props for a native terminal transcript. */ +export type TerminalProps = Omit & { + readonly clipboard?: ClipboardService; + readonly copyable?: boolean; + readonly copyLabels?: TerminalCopyLabels; + readonly lines: readonly TerminalLine[]; + readonly onCopyError?: (error: unknown) => void; + readonly onCopySuccess?: () => void; + readonly prompt?: string; + readonly ref?: Ref; + readonly title: string; +}; + +/** Props for parsing a simple native terminal transcript. */ +export type SimpleTerminalProps = Omit & { + readonly children: string; +}; + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + header: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + minHeight: 44, + }, + line: { alignItems: "flex-start", flexDirection: "row" }, + root: { borderWidth: 1, overflow: "hidden", width: "100%" }, +}); + +function getCommands(lines: readonly TerminalLine[]): string { + return lines + .filter((line) => line.type === "command") + .map((line) => line.content) + .join("\n"); +} + +type TerminalHeaderProps = { + readonly available: boolean; + readonly copyLabel?: string; + readonly onCopy: () => void; + readonly showCopy: boolean; + readonly title: string; +}; + +function TerminalHeader({ + available, + copyLabel, + onCopy, + showCopy, + title, +}: TerminalHeaderProps) { + const theme = useTheme(); + return ( + + + {title} + + {showCopy && copyLabel ? ( + [ + styles.action, + { + backgroundColor: pressed + ? theme.colors.accent + : theme.colors.muted, + borderRadius: theme.radius.md, + opacity: available ? 1 : 0.5, + paddingHorizontal: theme.spacing[3], + }, + ]} + > + + {copyLabel} + + + ) : null} + + ); +} +TerminalHeader.displayName = "TerminalHeader"; + +function TerminalLineView({ + line, + prompt, +}: { + readonly line: TerminalLine; + readonly prompt: string; +}) { + const theme = useTheme(); + const content = + line.type === "comment" + ? `${prompt === "$" ? "#" : prompt} ${line.content}` + : line.content; + return ( + + {line.type === "command" ? ( + + {prompt} + + ) : null} + + {content} + + + ); +} +TerminalLineView.displayName = "TerminalLineView"; + +function TerminalLines({ + lines, + prompt, +}: { + readonly lines: readonly TerminalLine[]; + readonly prompt: string; +}) { + const theme = useTheme(); + return ( + + + {lines.map((line, index) => ( + + ))} + + + ); +} +TerminalLines.displayName = "TerminalLines"; + +function Terminal({ + clipboard, + copyable = true, + copyLabels, + lines, + onCopyError, + onCopySuccess, + prompt = "$", + ref, + style, + title, + ...props +}: TerminalProps) { + const theme = useTheme(); + const [copiedCommands, setCopiedCommands] = useState(); + const commands = useMemo(() => getCommands(lines), [lines]); + const copyAvailable = clipboard !== undefined; + const copied = copiedCommands === commands; + const copyLabel = copied + ? copyLabels?.copied + : copyAvailable + ? copyLabels?.copy + : copyLabels?.unavailable; + const copy = async () => { + if (!clipboard) return; + try { + await clipboard.setText(commands); + setCopiedCommands(commands); + onCopySuccess?.(); + } catch (error: unknown) { + onCopyError?.(error); + } + }; + return ( + + void copy()} + showCopy={ + copyable ? commands.length > 0 && copyLabels !== undefined : false + } + title={title} + /> + + + ); +} +Terminal.displayName = "Terminal"; + +function parseTranscript(children: string): readonly TerminalLine[] { + return children + .trim() + .split("\n") + .map((line) => { + if (line.startsWith("$ ")) + return { content: line.slice(2), type: "command" }; + if (line.startsWith("# ")) + return { content: line.slice(2), type: "comment" }; + return { content: line, type: "output" }; + }); +} + +/** Parses `$ ` commands and `# ` comments into a native terminal transcript. */ +function SimpleTerminal({ children, ...props }: SimpleTerminalProps) { + return ; +} +SimpleTerminal.displayName = "SimpleTerminal"; + +export { SimpleTerminal, Terminal }; diff --git a/packages/ui-native/src/components/text-animate/text-animate.tsx b/packages/ui-native/src/components/text-animate/text-animate.tsx new file mode 100644 index 00000000..de868524 --- /dev/null +++ b/packages/ui-native/src/components/text-animate/text-animate.tsx @@ -0,0 +1,147 @@ +"use client"; + +import { type Ref, useEffect, useMemo, useState } from "react"; + +import { + Animated, + Easing, + StyleSheet, + type View, + type ViewProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Portable native entrance styles. Blur falls back to opacity. */ +export type TextAnimateAnimation = "blur" | "fade" | "slide-up"; + +/** Props for explicitly controlled staggered native text. */ +export type TextAnimateProps = Omit & { + readonly animation?: TextAnimateAnimation; + readonly by?: "character" | "word"; + readonly children: string; + readonly delay?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly visible?: boolean; +}; + +const styles = StyleSheet.create({ + root: { flexDirection: "row", flexWrap: "wrap" }, +}); + +function splitText(text: string, by: "character" | "word"): readonly string[] { + if (by === "character") return text.match(/[\s\S]/gu) ?? []; + return text.split(/(\s+)/).filter((segment) => segment.length > 0); +} + +function Segment({ + animation, + index, + progress, + total, + value, +}: { + readonly animation: TextAnimateAnimation; + readonly index: number; + readonly progress: Animated.Value; + readonly total: number; + readonly value: string; +}) { + const theme = useTheme(); + const start = total <= 1 ? 0 : (index / total) * 0.8; + const inputRange = [start, Math.min(1, start + 0.2)]; + return ( + + {value} + + ); +} +Segment.displayName = "Segment"; + +/** Reveals segments when `visible` changes; hosts own viewport detection. */ +function TextAnimate({ + accessibilityLabel, + animation = "fade", + by = "word", + children, + delay = 60, + reducedMotionService, + ref, + style, + visible = true, + ...props +}: TextAnimateProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const segments = useMemo(() => splitText(children, by), [by, children]); + const [progress, setProgress] = useState( + () => new Animated.Value(visible ? 1 : 0), + ); + void setProgress; + const staggerMs = Math.max(0, delay) * Math.max(0, segments.length - 1); + useEffect(() => { + if (reduceMotion) { + progress.setValue(visible ? 1 : 0); + return; + } + const animationHandle = Animated.timing(progress, { + duration: theme.motion.duration.base + staggerMs, + easing: Easing.out(Easing.cubic), + toValue: visible ? 1 : 0, + useNativeDriver: true, + }); + animationHandle.start(); + return () => { + animationHandle.stop(); + }; + }, [progress, reduceMotion, staggerMs, theme.motion.duration.base, visible]); + return ( + + {segments.map((segment, index) => ( + + ))} + + ); +} +TextAnimate.displayName = "TextAnimate"; + +export { TextAnimate }; diff --git a/packages/ui-native/src/components/text-reveal/text-reveal.tsx b/packages/ui-native/src/components/text-reveal/text-reveal.tsx new file mode 100644 index 00000000..0f2f3d71 --- /dev/null +++ b/packages/ui-native/src/components/text-reveal/text-reveal.tsx @@ -0,0 +1,81 @@ +"use client"; + +import type { Ref } from "react"; +import { + StyleSheet, + Text as NativeText, + View, + type ViewProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for native word highlighting driven by host-owned progress. */ +export type TextRevealProps = Omit & { + readonly children: string; + readonly progress?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; +}; + +const styles = StyleSheet.create({ + root: { flexDirection: "row", flexWrap: "wrap" }, +}); + +function clamp(value: number): number { + return Math.min(1, Math.max(0, value)); +} + +function wordOpacity(progress: number, total: number, index: number): number { + return Math.min(1, Math.max(0.2, progress * total - index)); +} + +/** Brightens words from explicit progress; native scroll ownership stays external. */ +function TextReveal({ + accessibilityLabel, + children, + progress = 1, + reducedMotionService, + ref, + style, + ...props +}: TextRevealProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const words = children.split(/\s+/).filter((word) => word.length > 0); + const resolvedProgress = reduceMotion ? 1 : clamp(progress); + + return ( + + {words.map((word, index) => ( + + {word} + + ))} + + ); +} +TextReveal.displayName = "TextReveal"; + +export { TextReveal }; diff --git a/packages/ui-native/src/components/text-shimmer/text-shimmer.tsx b/packages/ui-native/src/components/text-shimmer/text-shimmer.tsx new file mode 100644 index 00000000..d4710cc7 --- /dev/null +++ b/packages/ui-native/src/components/text-shimmer/text-shimmer.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { type Ref, useEffect, useState } from "react"; + +import { + Animated, + Easing, + type Text as NativeTextInstance, + type TextProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for native text that cycles between semantic foreground tones. */ +export type TextShimmerProps = Omit & { + readonly children: string; + readonly duration?: number; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; +}; + +/** Provides a dependency-free color cycle in place of web gradient clipping. */ +function TextShimmer({ + children, + duration = 2000, + reducedMotionService, + ref, + style, + ...props +}: TextShimmerProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const [progress, setProgress] = useState(() => new Animated.Value(0)); + void setProgress; + + useEffect(() => { + if (reduceMotion) { + progress.setValue(1); + return; + } + const animation = Animated.loop( + Animated.sequence([ + Animated.timing(progress, { + duration: Math.max(theme.motion.duration.base, duration / 2), + easing: Easing.inOut(Easing.cubic), + toValue: 1, + useNativeDriver: false, + }), + Animated.timing(progress, { + duration: Math.max(theme.motion.duration.base, duration / 2), + easing: Easing.inOut(Easing.cubic), + toValue: 0, + useNativeDriver: false, + }), + ]), + ); + animation.start(); + return () => { + animation.stop(); + }; + }, [duration, progress, reduceMotion, theme.motion.duration.base]); + + return ( + + {children} + + ); +} +TextShimmer.displayName = "TextShimmer"; + +export { TextShimmer }; diff --git a/packages/ui-native/src/components/typewriter/typewriter.tsx b/packages/ui-native/src/components/typewriter/typewriter.tsx new file mode 100644 index 00000000..1c2b14fd --- /dev/null +++ b/packages/ui-native/src/components/typewriter/typewriter.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { type Ref, useEffect, useState } from "react"; + +import { + Text as NativeText, + type Text as NativeTextInstance, + type TextProps, +} from "react-native"; + +import { + type ReducedMotionService, + useReducedMotion, +} from "../../primitives/use-reduced-motion"; +import { useTheme } from "../../theme/theme-provider"; + +/** Props for deterministic native typewriter text. */ +export type TypewriterProps = Omit & { + readonly cursor?: false | string; + readonly reducedMotionService?: ReducedMotionService; + readonly ref?: Ref; + readonly speed?: number; + readonly text: string; +}; + +function useTypedCount( + reduceMotion: boolean, + speed: number, + text: string, +): number { + const [state, setState] = useState({ + count: text.length, + reduceMotion, + text, + }); + if (state.reduceMotion !== reduceMotion || state.text !== text) { + setState({ count: reduceMotion ? text.length : 0, reduceMotion, text }); + } + useEffect(() => { + if (reduceMotion || text.length === 0) return; + const timer = setInterval( + () => { + setState((current) => + current.count >= text.length + ? current + : { ...current, count: current.count + 1 }, + ); + }, + Math.max(1, speed), + ); + return () => { + clearInterval(timer); + }; + }, [reduceMotion, speed, text]); + const current = state.reduceMotion === reduceMotion && state.text === text; + return current ? state.count : reduceMotion ? text.length : 0; +} + +/** Types characters on a fixed interval and exposes the complete accessible text. */ +function Typewriter({ + accessibilityLabel, + cursor = "|", + reducedMotionService, + ref, + speed = 60, + style, + text, + ...props +}: TypewriterProps) { + const theme = useTheme(); + const reduceMotion = useReducedMotion(reducedMotionService); + const count = useTypedCount(reduceMotion, speed, text); + const typing = !reduceMotion && count < text.length; + return ( + + {reduceMotion ? text : text.slice(0, count)} + {cursor !== false && typing ? ( + + {cursor} + + ) : null} + + ); +} +Typewriter.displayName = "Typewriter"; + +export { Typewriter }; From 394d5d90cc21e2e6b69bf146c0bcf863b7f73c48 Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:33:29 +0200 Subject: [PATCH 05/18] pi-agent: Native AI composites --- .../agent-activity/agent-activity.tsx | 545 +++++++++++++++ .../ai-chat-input/ai-chat-input.tsx | 347 ++++++++++ .../chain-of-thought/chain-of-thought.tsx | 216 ++++++ .../conversation-thread.tsx | 645 ++++++++++++++++++ .../model-selector/model-selector.tsx | 551 +++++++++++++++ .../components/native-ai-components.test.tsx | 290 ++++++++ .../components/prompt-input/prompt-input.tsx | 370 ++++++++++ .../src/components/reasoning/reasoning.tsx | 212 ++++++ .../thinking-block/thinking-block.tsx | 142 ++++ 9 files changed, 3318 insertions(+) create mode 100644 packages/ui-native/src/components/agent-activity/agent-activity.tsx create mode 100644 packages/ui-native/src/components/ai-chat-input/ai-chat-input.tsx create mode 100644 packages/ui-native/src/components/chain-of-thought/chain-of-thought.tsx create mode 100644 packages/ui-native/src/components/conversation-thread/conversation-thread.tsx create mode 100644 packages/ui-native/src/components/model-selector/model-selector.tsx create mode 100644 packages/ui-native/src/components/native-ai-components.test.tsx create mode 100644 packages/ui-native/src/components/prompt-input/prompt-input.tsx create mode 100644 packages/ui-native/src/components/reasoning/reasoning.tsx create mode 100644 packages/ui-native/src/components/thinking-block/thinking-block.tsx diff --git a/packages/ui-native/src/components/agent-activity/agent-activity.tsx b/packages/ui-native/src/components/agent-activity/agent-activity.tsx new file mode 100644 index 00000000..4a665d10 --- /dev/null +++ b/packages/ui-native/src/components/agent-activity/agent-activity.tsx @@ -0,0 +1,545 @@ +"use client"; + +import { + Children, + createContext, + isValidElement, + type ReactNode, + type Ref, + use, + useCallback, + useId, + useMemo, + useState, +} from "react"; + +import { + Pressable, + StyleSheet, + type Text as NativeText, + Text, + type TextProps, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** State of one native agent step, including unavailable services. */ +export type AgentStepStatus = + | "completed" + | "error" + | "pending" + | "running" + | "skipped" + | "unavailable"; + +/** State of the native agent activity surface. */ +export type AgentActivityStatus = + | "completed" + | "error" + | "idle" + | "running" + | "unavailable"; + +/** Caller-localized activity copy and status names. */ +export type AgentActivityLabels = { + readonly activity: string; + readonly collapse: string; + readonly elapsed: string; + readonly expand: string; + readonly status: Readonly< + Record + >; +}; + +/** Props for the native agent activity surface. */ +export type AgentActivityProps = ViewProps & { + readonly elapsed?: ReactNode; + readonly labels: AgentActivityLabels; + readonly ref?: Ref; + readonly status?: AgentActivityStatus; +}; + +/** Props for one native agent activity step. */ +export type AgentStepProps = ViewProps & { + readonly defaultOpen?: boolean; + readonly icon?: ReactNode; + readonly onOpenChange?: (open: boolean) => void; + readonly open?: boolean; + readonly ref?: Ref; + readonly status: AgentStepStatus; +}; + +export type AgentStepTitleProps = TextProps & { + readonly ref?: Ref; +}; +export type AgentStepDurationProps = TextProps & { + readonly ref?: Ref; +}; +export type AgentStepDetailProps = ViewProps & { readonly ref?: Ref }; + +/** Props for native step progress. */ +export type AgentStepProgressProps = Omit & { + readonly label: string; + readonly ref?: Ref; + readonly value: number; +}; + +type StepContextValue = { readonly status: AgentStepStatus }; +const StepContext = createContext({ status: "pending" }); +const LabelsContext = createContext(null); + +function useLabels(): AgentActivityLabels { + const labels = use(LabelsContext); + if (!labels) { + throw new Error( + "AgentActivity parts must be rendered inside AgentActivity.", + ); + } + return labels; +} + +const styles = StyleSheet.create({ + activity: { + borderWidth: 1, + }, + detail: { + borderTopWidth: 1, + }, + header: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + }, + icon: { + alignItems: "center", + justifyContent: "center", + minHeight: 24, + minWidth: 24, + }, + pressed: { + opacity: 0.8, + }, + progress: { + height: 6, + overflow: "hidden", + }, + progressValue: { + height: "100%", + }, + step: { + borderWidth: 1, + overflow: "hidden", + }, + stepHeader: { + alignItems: "flex-start", + flexDirection: "row", + }, + stepHeaderContent: { + flex: 1, + }, + steps: { + flexDirection: "column", + }, + toggle: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, +}); + +function getStepColor( + status: AgentStepStatus, + colors: { + readonly destructive: string; + readonly mutedForeground: string; + readonly primary: string; + }, +): string { + if (status === "error" || status === "unavailable") { + return colors.destructive; + } + if (status === "running" || status === "completed") return colors.primary; + return colors.mutedForeground; +} + +function ActivityHeader({ + elapsed, + labels, + status, +}: { + readonly elapsed?: ReactNode; + readonly labels: AgentActivityLabels; + readonly status: AgentActivityStatus; +}) { + const theme = useTheme(); + const failed = status === "error" || status === "unavailable"; + return ( + + + + {labels.activity} + + + {labels.status[status]} + + + {elapsed ? ( + {elapsed} + ) : null} + + ); +} +ActivityHeader.displayName = "ActivityHeader"; + +/** Native activity surface for agent steps and service state. */ +function AgentActivity({ + children, + elapsed, + labels, + ref, + status = "idle", + style, + ...props +}: AgentActivityProps) { + const theme = useTheme(); + return ( + + + + + {children} + + + + ); +} +AgentActivity.displayName = "AgentActivity"; + +function AgentStepDetail({ ref, style, ...props }: AgentStepDetailProps) { + const theme = useTheme(); + return ( + + ); +} +AgentStepDetail.displayName = "AgentStepDetail"; + +function splitStepChildren(children: ReactNode): { + readonly details: ReactNode[]; + readonly header: ReactNode[]; +} { + const details: ReactNode[] = []; + const header: ReactNode[] = []; + Children.forEach(children, (child) => { + if (isValidElement(child) && child.type === AgentStepDetail) { + details.push(child); + } else { + header.push(child); + } + }); + return { details, header }; +} + +type StepHeaderProps = { + readonly hasDetails: boolean; + readonly header: ReactNode; + readonly icon?: ReactNode; + readonly isOpen: boolean; + readonly labels: AgentActivityLabels; + readonly onToggle: () => void; + readonly status: AgentStepStatus; + readonly statusColor: string; +}; + +function StepHeader({ + hasDetails, + header, + icon, + isOpen, + labels, + onToggle, + status, + statusColor, +}: StepHeaderProps) { + const theme = useTheme(); + return ( + + + {icon} + + + {header} + + {labels.status[status]} + + + {hasDetails ? ( + [ + styles.toggle, + { borderRadius: theme.radius.sm }, + pressed ? styles.pressed : undefined, + ]} + > + + {isOpen ? labels.collapse : labels.expand} + + + ) : null} + + ); +} +StepHeader.displayName = "StepHeader"; + +/** One status-aware row in a native agent activity surface. */ +function AgentStep({ + children, + defaultOpen = true, + icon, + onOpenChange, + open, + ref, + status, + style, + ...props +}: AgentStepProps) { + const theme = useTheme(); + const labels = useLabels(); + const [internalOpen, setInternalOpen] = useState(defaultOpen); + const controlled = open !== undefined; + const isOpen = controlled ? open : internalOpen; + const detailId = useId(); + const split = useMemo(() => splitStepChildren(children), [children]); + const hasDetails = split.details.length > 0; + const statusColor = getStepColor(status, theme.colors); + + const handleToggle = useCallback(() => { + const next = !isOpen; + if (!controlled) setInternalOpen(next); + onOpenChange?.(next); + }, [controlled, isOpen, onOpenChange]); + + const context = useMemo(() => ({ status }), [status]); + + return ( + + + + {hasDetails && isOpen ? ( + + {split.details} + + ) : null} + + + ); +} +AgentStep.displayName = "AgentStep"; + +/** Primary text for a native agent step. */ +function AgentStepTitle({ ref, style, ...props }: AgentStepTitleProps) { + const theme = useTheme(); + return ( + + ); +} +AgentStepTitle.displayName = "AgentStepTitle"; + +/** Caller-formatted duration for a native agent step. */ +function AgentStepDuration({ ref, style, ...props }: AgentStepDurationProps) { + const theme = useTheme(); + return ( + + ); +} +AgentStepDuration.displayName = "AgentStepDuration"; + +/** Token-driven progress for a native agent step. */ +function AgentStepProgress({ + label, + ref, + style, + value, + ...props +}: AgentStepProgressProps) { + const theme = useTheme(); + const clamped = Math.max(0, Math.min(100, value)); + return ( + + + + ); +} +AgentStepProgress.displayName = "AgentStepProgress"; + +/** Supporting text block inside a native agent step detail region. */ +function AgentStepDetailText({ ref, style, ...props }: AgentStepTitleProps) { + const theme = useTheme(); + return ( + + ); +} +AgentStepDetailText.displayName = "AgentStepDetailText"; + +/** Reads the nearest native agent step status. */ +function useAgentStepStatus(): AgentStepStatus { + return use(StepContext).status; +} + +export { + AgentActivity, + AgentStep, + AgentStepDetail, + AgentStepDetailText, + AgentStepDuration, + AgentStepProgress, + AgentStepTitle, + useAgentStepStatus, +}; diff --git a/packages/ui-native/src/components/ai-chat-input/ai-chat-input.tsx b/packages/ui-native/src/components/ai-chat-input/ai-chat-input.tsx new file mode 100644 index 00000000..9076230d --- /dev/null +++ b/packages/ui-native/src/components/ai-chat-input/ai-chat-input.tsx @@ -0,0 +1,347 @@ +"use client"; + +import { type ReactNode, type Ref, useCallback, useId, useState } from "react"; + +import { + Pressable, + StyleSheet, + Text, + TextInput, + type TextInputProps, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Explicit availability of the service receiving a chat message. */ +export type AIChatServiceState = + | { readonly message: string; readonly status: "unavailable" } + | { readonly status: "available" }; + +/** Props for the native chat composer. */ +export type AIChatInputProps = Omit & { + readonly defaultValue?: string; + readonly disabled?: boolean; + readonly helperText?: string; + readonly inputLabel: string; + readonly inputProps?: Omit< + TextInputProps, + | "editable" + | "multiline" + | "onChangeText" + | "onSubmitEditing" + | "submitBehavior" + | "value" + >; + readonly isSubmitting?: boolean; + readonly onSubmit?: (value: string) => void; + readonly onValueChange?: (value: string) => void; + readonly ref?: Ref; + readonly serviceState?: AIChatServiceState; + readonly status?: string; + readonly submitLabel: string; + readonly toolbar?: ReactNode; + readonly value?: string; +}; + +const styles = StyleSheet.create({ + footer: { alignItems: "flex-end", borderTopWidth: 1, flexDirection: "row" }, + input: { minHeight: 120, padding: 0, textAlignVertical: "top" }, + messages: { flex: 1 }, + pressed: { opacity: 0.8 }, + root: { borderWidth: 1 }, + submit: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + toolbar: { alignItems: "center", flexDirection: "row", flexWrap: "wrap" }, +}); + +type ComposerState = { + readonly canSubmit: boolean; + readonly currentValue: string; + readonly handleSubmit: () => void; + readonly handleValueChange: (value: string) => void; + readonly unavailable: boolean; +}; + +function useComposerState(props: AIChatInputProps): ComposerState { + const [internalValue, setInternalValue] = useState(props.defaultValue ?? ""); + const controlled = props.value !== undefined; + const currentValue = controlled ? props.value : internalValue; + const unavailable = props.serviceState?.status === "unavailable"; + const canSubmit = + props.disabled !== true && + props.isSubmitting !== true && + !unavailable && + currentValue.trim().length > 0; + const handleValueChange = useCallback( + (nextValue: string) => { + if (!controlled) setInternalValue(nextValue); + props.onValueChange?.(nextValue); + }, + [controlled, props], + ); + const handleSubmit = useCallback(() => { + if (!canSubmit) return; + props.onSubmit?.(currentValue); + if (!controlled) setInternalValue(""); + }, [canSubmit, controlled, currentValue, props]); + return { + canSubmit, + currentValue, + handleSubmit, + handleValueChange, + unavailable, + }; +} + +function ComposerMessages({ + currentValue, + helperText, + inputProps, + serviceState, + status, +}: Pick< + AIChatInputProps, + "helperText" | "inputProps" | "serviceState" | "status" +> & { readonly currentValue: string }) { + const theme = useTheme(); + const unavailable = serviceState?.status === "unavailable"; + return ( + + {[helperText, status].map((message) => + message ? ( + + {message} + + ) : null, + )} + {unavailable ? ( + + {serviceState.message} + + ) : null} + {typeof inputProps?.maxLength === "number" ? ( + + {currentValue.length}/{inputProps.maxLength} + + ) : null} + + ); +} +ComposerMessages.displayName = "ComposerMessages"; + +function SubmitAction({ + canSubmit, + isSubmitting, + label, + onPress, +}: { + readonly canSubmit: boolean; + readonly isSubmitting: boolean; + readonly label: string; + readonly onPress: () => void; +}) { + const theme = useTheme(); + return ( + [ + styles.submit, + { + backgroundColor: theme.colors.primary, + borderRadius: theme.radius.full, + opacity: canSubmit ? 1 : 0.5, + paddingHorizontal: theme.spacing[4], + }, + pressed ? styles.pressed : undefined, + ]} + > + + {label} + + + ); +} +SubmitAction.displayName = "SubmitAction"; + +type ComposerBodyProps = { + readonly disabled: boolean; + readonly inputLabel: string; + readonly inputProps?: AIChatInputProps["inputProps"]; + readonly state: ComposerState; +}; + +function ComposerBody({ + disabled, + inputLabel, + inputProps, + state, +}: ComposerBodyProps) { + const theme = useTheme(); + const inputId = useId(); + return ( + + ); +} +ComposerBody.displayName = "ComposerBody"; + +function ComposerShell({ + children, + reference, + style, + viewProps, +}: { + readonly children: ReactNode; + readonly reference?: Ref; + readonly style?: AIChatInputProps["style"]; + readonly viewProps: ViewProps; +}) { + const theme = useTheme(); + return ( + + {children} + + ); +} +ComposerShell.displayName = "ComposerShell"; + +/** Accessible native multiline chat composer with submit-key handling. */ +function AIChatInput({ + defaultValue, + disabled = false, + helperText, + inputLabel, + inputProps, + isSubmitting = false, + onSubmit, + onValueChange, + ref, + serviceState, + status, + style, + submitLabel, + toolbar, + value, + ...viewProps +}: AIChatInputProps) { + const theme = useTheme(); + const stateProps = { + defaultValue, + disabled, + helperText, + inputLabel, + inputProps, + isSubmitting, + onSubmit, + onValueChange, + serviceState, + status, + submitLabel, + toolbar, + value, + }; + const state = useComposerState(stateProps); + return ( + + + {toolbar ? ( + + {toolbar} + + ) : null} + + + + + + ); +} +AIChatInput.displayName = "AIChatInput"; + +export { AIChatInput }; diff --git a/packages/ui-native/src/components/chain-of-thought/chain-of-thought.tsx b/packages/ui-native/src/components/chain-of-thought/chain-of-thought.tsx new file mode 100644 index 00000000..f42de286 --- /dev/null +++ b/packages/ui-native/src/components/chain-of-thought/chain-of-thought.tsx @@ -0,0 +1,216 @@ +import type { Ref } from "react"; +import { + StyleSheet, + Text, + type TextStyle, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** State of one ordered reasoning step. */ +export type ChainOfThoughtStatus = "active" | "complete" | "error" | "pending"; + +/** One text step in a native reasoning sequence. */ +export type ChainOfThoughtStep = { + readonly description?: string; + readonly id: string; + readonly status?: ChainOfThoughtStatus; + readonly title: string; +}; + +/** Caller-localized status names announced and rendered for each step. */ +export type ChainOfThoughtStatusLabels = Readonly< + Record +>; + +/** Props for the native ordered reasoning sequence. */ +export type ChainOfThoughtProps = Omit & { + readonly ref?: Ref; + readonly statusLabels: ChainOfThoughtStatusLabels; + readonly steps: readonly ChainOfThoughtStep[]; +}; + +const styles = StyleSheet.create({ + content: { flex: 1 }, + item: { alignItems: "flex-start", flexDirection: "row" }, + marker: { + alignItems: "center", + borderWidth: 1, + justifyContent: "center", + minHeight: 24, + minWidth: 24, + }, + rail: { alignSelf: "center", flex: 1, minHeight: 16, width: 1 }, + railColumn: { alignSelf: "stretch" }, +}); + +function getStatusColor( + status: ChainOfThoughtStatus, + colors: { + readonly destructive: string; + readonly mutedForeground: string; + readonly primary: string; + }, +): string { + if (status === "error") return colors.destructive; + if (status === "active" || status === "complete") return colors.primary; + return colors.mutedForeground; +} + +function StepMarker({ + index, + isLast, + statusColor, +}: { + readonly index: number; + readonly isLast: boolean; + readonly statusColor: string; +}) { + const theme = useTheme(); + return ( + + + + {index + 1} + + + {isLast ? null : ( + + )} + + ); +} +StepMarker.displayName = "StepMarker"; + +function StepText({ + isLast, + status, + statusColor, + statusLabels, + step, +}: { + readonly isLast: boolean; + readonly status: ChainOfThoughtStatus; + readonly statusColor: string; + readonly statusLabels: ChainOfThoughtStatusLabels; + readonly step: ChainOfThoughtStep; +}) { + const theme = useTheme(); + const titleStyle: TextStyle = { + color: + status === "pending" + ? theme.colors.mutedForeground + : theme.colors.foreground, + fontWeight: theme.typography.fontWeight.caption, + }; + return ( + + + {step.title} + + + {statusLabels[status]} + + {step.description ? ( + + {step.description} + + ) : null} + + ); +} +StepText.displayName = "StepText"; + +function ChainOfThoughtItem({ + index, + isLast, + statusLabels, + step, +}: { + readonly index: number; + readonly isLast: boolean; + readonly statusLabels: ChainOfThoughtStatusLabels; + readonly step: ChainOfThoughtStep; +}) { + const theme = useTheme(); + const status = step.status ?? "pending"; + const statusColor = getStatusColor(status, theme.colors); + return ( + + + + + ); +} +ChainOfThoughtItem.displayName = "ChainOfThoughtItem"; + +/** Native ordered, status-aware reasoning sequence. */ +function ChainOfThought({ + accessibilityLabel, + ref, + statusLabels, + steps, + style, + ...props +}: ChainOfThoughtProps) { + return ( + + {steps.map((step, index) => ( + + ))} + + ); +} +ChainOfThought.displayName = "ChainOfThought"; + +export { ChainOfThought }; diff --git a/packages/ui-native/src/components/conversation-thread/conversation-thread.tsx b/packages/ui-native/src/components/conversation-thread/conversation-thread.tsx new file mode 100644 index 00000000..93da2bea --- /dev/null +++ b/packages/ui-native/src/components/conversation-thread/conversation-thread.tsx @@ -0,0 +1,645 @@ +"use client"; + +import { + createContext, + type Ref, + use, + useCallback, + useMemo, + useRef, + useState, +} from "react"; + +import { + type NativeScrollEvent, + type NativeSyntheticEvent, + Pressable, + ScrollView, + StyleSheet, + type Text as NativeText, + Text, + type TextProps, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; +import { + ThinkingBlock, + type ThinkingBlockLabels, +} from "../thinking-block/thinking-block"; + +/** A tool invocation associated with an assistant message. */ +export type ToolCall = { + readonly id: string; + readonly input?: Readonly>; + readonly name: string; + readonly result?: string; +}; + +/** One stable message in a native conversation. */ +export type ConversationMessage = { + readonly content: string; + readonly id: string; + readonly isStreaming?: boolean; + readonly role: "assistant" | "user"; + readonly thinking?: string; + readonly toolCalls?: readonly ToolCall[]; +}; + +/** One stable, caller-localized suggested prompt. */ +export type ConversationSuggestion = { + readonly id: string; + readonly label: string; + readonly value: string; +}; + +/** Caller-localized labels for conversation state and actions. */ +export type ConversationThreadLabels = { + readonly assistantMessage: string; + readonly assistantTyping: string; + readonly negativeFeedback: string; + readonly positiveFeedback: string; + readonly retry: string; + readonly scrollToBottom: string; + readonly toolCalls: string; + readonly userMessage: string; +}; + +/** Props for the native conversation provider. */ +export type ConversationThreadProps = ViewProps & { + readonly isStreaming?: boolean; + readonly labels: ConversationThreadLabels; + readonly messages: readonly ConversationMessage[]; + readonly onFeedback?: ( + messageId: string, + feedback: "negative" | "positive", + ) => void; + readonly onRetry?: (messageId: string) => void; + readonly onSend?: (message: string) => void; + readonly ref?: Ref; + readonly thinkingLabels: ThinkingBlockLabels; +}; + +export type ConversationHeaderProps = ViewProps & { readonly ref?: Ref }; +export type ConversationTitleProps = TextProps & { + readonly ref?: Ref; +}; +export type ConversationMessagesProps = ViewProps & { + readonly ref?: Ref; +}; +export type ConversationEmptyProps = ViewProps & { readonly ref?: Ref }; +export type ConversationSuggestionsProps = Omit & { + readonly ref?: Ref; + readonly suggestions?: readonly ConversationSuggestion[]; +}; +export type ConversationScrollButtonProps = Omit & { + readonly ref?: Ref; +}; +export type ConversationLoadingProps = Omit & { + readonly ref?: Ref; +}; + +type ConversationContextValue = { + readonly isAtBottom: boolean; + readonly isStreaming: boolean; + readonly labels: ConversationThreadLabels; + readonly messages: readonly ConversationMessage[]; + readonly onFeedback?: ConversationThreadProps["onFeedback"]; + readonly onRetry?: ConversationThreadProps["onRetry"]; + readonly onScroll: (event: NativeSyntheticEvent) => void; + readonly onSend?: ConversationThreadProps["onSend"]; + readonly scrollToBottom: () => void; + readonly scrollViewRef: Ref; + readonly thinkingLabels: ThinkingBlockLabels; +}; + +const ConversationContext = createContext( + null, +); + +function useConversation(): ConversationContextValue { + const context = use(ConversationContext); + if (!context) { + throw new Error( + "ConversationThread parts must be rendered inside ConversationThread.", + ); + } + return context; +} + +const styles = StyleSheet.create({ + action: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + actions: { + alignItems: "center", + flexDirection: "row", + flexWrap: "wrap", + }, + bubble: { + maxWidth: "80%", + }, + empty: { + alignItems: "center", + justifyContent: "center", + }, + header: { + alignItems: "center", + borderBottomWidth: 1, + flexDirection: "row", + }, + message: { + flexDirection: "row", + }, + messageAssistant: { + justifyContent: "flex-start", + }, + messages: { + flex: 1, + }, + messageUser: { + justifyContent: "flex-end", + }, + pressed: { + opacity: 0.8, + }, + root: { + flex: 1, + overflow: "hidden", + }, + scrollButton: { + alignItems: "center", + alignSelf: "flex-end", + borderWidth: 1, + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + suggestion: { + alignItems: "center", + borderWidth: 1, + justifyContent: "center", + minHeight: 44, + }, + suggestions: { + alignItems: "center", + flexDirection: "row", + flexWrap: "wrap", + justifyContent: "center", + }, + toolList: { + borderLeftWidth: 1, + }, +}); + +function MessageAction({ + label, + onPress, +}: { + readonly label: string; + readonly onPress: () => void; +}) { + const theme = useTheme(); + return ( + [ + styles.action, + { borderRadius: theme.radius.sm, paddingHorizontal: theme.spacing[2] }, + pressed ? styles.pressed : undefined, + ]} + > + + {label} + + + ); +} +MessageAction.displayName = "MessageAction"; + +function MessageTools({ + label, + toolCalls, +}: { + readonly label: string; + readonly toolCalls?: readonly ToolCall[]; +}) { + const theme = useTheme(); + if (!toolCalls || toolCalls.length === 0) return null; + return ( + + {toolCalls.map((toolCall) => ( + + {toolCall.name} + + ))} + + ); +} +MessageTools.displayName = "MessageTools"; + +function MessageActions({ messageId }: { readonly messageId: string }) { + const theme = useTheme(); + const { labels, onFeedback, onRetry } = useConversation(); + if (!onRetry && !onFeedback) return null; + return ( + + {onRetry ? ( + { + onRetry(messageId); + }} + /> + ) : null} + {onFeedback ? ( + <> + { + onFeedback(messageId, "positive"); + }} + /> + { + onFeedback(messageId, "negative"); + }} + /> + + ) : null} + + ); +} +MessageActions.displayName = "MessageActions"; + +function MessageItem({ message }: { readonly message: ConversationMessage }) { + const theme = useTheme(); + const { labels, thinkingLabels } = useConversation(); + const isUser = message.role === "user"; + const roleLabel = isUser ? labels.userMessage : labels.assistantMessage; + + return ( + + + {!isUser && message.thinking ? ( + + ) : null} + + + {message.content} + + {isUser ? null : } + + + ); +} +MessageItem.displayName = "MessageItem"; + +/** Root state provider for the native conversation compound family. */ +function ConversationThread({ + children, + isStreaming = false, + labels, + messages, + onFeedback, + onRetry, + onSend, + ref, + style, + thinkingLabels, + ...props +}: ConversationThreadProps) { + const scrollViewReference = useRef(null); + const [isAtBottom, setIsAtBottom] = useState(true); + + const scrollToBottom = useCallback(() => { + scrollViewReference.current?.scrollToEnd({ animated: false }); + }, []); + + const handleScroll = useCallback( + (event: NativeSyntheticEvent) => { + const { contentOffset, contentSize, layoutMeasurement } = + event.nativeEvent; + setIsAtBottom( + contentSize.height - contentOffset.y - layoutMeasurement.height <= 100, + ); + }, + [], + ); + + const context = useMemo( + () => ({ + isAtBottom, + isStreaming, + labels, + messages, + onFeedback, + onRetry, + onScroll: handleScroll, + onSend, + scrollToBottom, + scrollViewRef: scrollViewReference, + thinkingLabels, + }), + [ + handleScroll, + isAtBottom, + isStreaming, + labels, + messages, + onFeedback, + onRetry, + onSend, + scrollToBottom, + thinkingLabels, + ], + ); + + return ( + + + {children} + + + ); +} +ConversationThread.displayName = "ConversationThread"; + +/** Header region above a native conversation. */ +function ConversationHeader({ ref, style, ...props }: ConversationHeaderProps) { + const theme = useTheme(); + return ( + + ); +} +ConversationHeader.displayName = "ConversationHeader"; + +/** Accessible heading text for a native conversation. */ +function ConversationTitle({ ref, style, ...props }: ConversationTitleProps) { + const theme = useTheme(); + return ( + + ); +} +ConversationTitle.displayName = "ConversationTitle"; + +/** Scrollable, live-updating native message list. */ +function ConversationMessages({ + children, + ref, + style, + ...props +}: ConversationMessagesProps) { + const theme = useTheme(); + const { labels, messages, onScroll, scrollToBottom, scrollViewRef } = + useConversation(); + return ( + + + + {messages.map((message) => ( + + ))} + + + {children} + + ); +} +ConversationMessages.displayName = "ConversationMessages"; + +/** Empty-state region rendered when the conversation has no messages. */ +function ConversationEmpty({ ref, style, ...props }: ConversationEmptyProps) { + const theme = useTheme(); + const { messages } = useConversation(); + if (messages.length > 0) return null; + return ( + + ); +} +ConversationEmpty.displayName = "ConversationEmpty"; + +/** Stable suggested prompts for a native conversation empty state. */ +function ConversationSuggestions({ + ref, + style, + suggestions = [], + ...props +}: ConversationSuggestionsProps) { + const theme = useTheme(); + const { onSend } = useConversation(); + return ( + + {suggestions.map((suggestion) => ( + onSend?.(suggestion.value)} + style={({ pressed }) => [ + styles.suggestion, + { + backgroundColor: theme.colors.background, + borderColor: theme.colors.border, + borderRadius: theme.radius.full, + paddingHorizontal: theme.spacing[4], + }, + pressed ? styles.pressed : undefined, + ]} + > + + {suggestion.label} + + + ))} + + ); +} +ConversationSuggestions.displayName = "ConversationSuggestions"; + +/** Action shown while the native message list is away from its end. */ +function ConversationScrollButton({ + ref, + style, + ...props +}: ConversationScrollButtonProps) { + const theme = useTheme(); + const { isAtBottom, labels, scrollToBottom } = useConversation(); + if (isAtBottom) return null; + return ( + + + {labels.scrollToBottom} + + + ); +} +ConversationScrollButton.displayName = "ConversationScrollButton"; + +/** Explicit text status shown while the assistant response is streaming. */ +function ConversationLoading({ + ref, + style, + ...props +}: ConversationLoadingProps) { + const theme = useTheme(); + const { isStreaming, labels, messages } = useConversation(); + const lastMessage = messages.at(-1); + if (!isStreaming || lastMessage?.role !== "assistant") return null; + return ( + + + {labels.assistantTyping} + + + ); +} +ConversationLoading.displayName = "ConversationLoading"; + +export { + ConversationEmpty, + ConversationHeader, + ConversationLoading, + ConversationMessages, + ConversationScrollButton, + ConversationSuggestions, + ConversationThread, + ConversationTitle, +}; diff --git a/packages/ui-native/src/components/model-selector/model-selector.tsx b/packages/ui-native/src/components/model-selector/model-selector.tsx new file mode 100644 index 00000000..d5d16214 --- /dev/null +++ b/packages/ui-native/src/components/model-selector/model-selector.tsx @@ -0,0 +1,551 @@ +"use client"; + +import { + type Ref, + useCallback, + useEffect, + useId, + useMemo, + useState, +} from "react"; + +import { + AccessibilityInfo, + Modal, + Pressable, + ScrollView, + StyleSheet, + Text, + TextInput, + type TextInputProps, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Explicit service availability for one selectable model. */ +export type ModelServiceState = + | { readonly message: string; readonly status: "unavailable" } + | { readonly status: "available" }; + +/** Model data rendered by the native selector. */ +export type ModelInfo = { + readonly description?: string; + readonly id: string; + readonly name: string; + readonly pricing?: { readonly input?: number; readonly output?: number }; + readonly serviceState?: ModelServiceState; +}; + +/** Caller-localized copy for the native model selector. */ +export type ModelSelectorLabels = { + readonly close: string; + readonly description: string; + readonly noModels: string; + readonly search: string; + readonly selected: string; + readonly title: string; + readonly unavailable: string; +}; + +/** Props for a native modal model selector. */ +export type ModelSelectorProps = Omit & { + readonly defaultOpen?: boolean; + readonly defaultSelectedModelId?: string; + readonly formatPricing?: (pricing: ModelInfo["pricing"]) => string; + readonly labels: ModelSelectorLabels; + readonly models: readonly ModelInfo[]; + readonly onOpenChange?: (open: boolean) => void; + readonly onSelectModel?: (modelId: string) => void; + readonly open?: boolean; + readonly ref?: Ref; + readonly searchInputProps?: Omit; + readonly selectedModelId?: string; +}; + +const styles = StyleSheet.create({ + close: { alignItems: "center", justifyContent: "center", minHeight: 44 }, + header: { alignItems: "flex-start", flexDirection: "row" }, + headerText: { flex: 1 }, + item: { borderBottomWidth: 1, justifyContent: "center", minHeight: 60 }, + modal: { flex: 1, justifyContent: "center" }, + panel: { borderWidth: 1, maxHeight: "80%", overflow: "hidden" }, + pressed: { opacity: 0.8 }, + search: { borderWidth: 1, minHeight: 44 }, +}); + +function useReducedMotion(): boolean { + const [reduced, setReduced] = useState(false); + useEffect(() => { + void AccessibilityInfo.isReduceMotionEnabled().then(setReduced); + const subscription = AccessibilityInfo.addEventListener( + "reduceMotionChanged", + setReduced, + ); + return () => { + subscription.remove(); + }; + }, []); + return reduced; +} + +function modelMatches(model: ModelInfo, query: string): boolean { + const normalized = query.trim().toLocaleLowerCase(); + if (normalized.length === 0) return true; + return ( + model.name.toLocaleLowerCase().includes(normalized) || + model.id.toLocaleLowerCase().includes(normalized) || + model.description?.toLocaleLowerCase().includes(normalized) === true + ); +} + +function ModelMeta({ + labels, + model, + price, + selected, +}: { + readonly labels: ModelSelectorLabels; + readonly model: ModelInfo; + readonly price?: string; + readonly selected: boolean; +}) { + const theme = useTheme(); + const unavailable = model.serviceState?.status === "unavailable"; + return ( + <> + + {model.id} + + {model.description ? ( + + {model.description} + + ) : null} + {price ? ( + + {price} + + ) : null} + {selected ? ( + + {labels.selected} + + ) : null} + {unavailable ? ( + + {labels.unavailable}: {model.serviceState.message} + + ) : null} + + ); +} +ModelMeta.displayName = "ModelMeta"; + +function ModelRow({ + formatPricing, + labels, + model, + onSelect, + selected, +}: { + readonly formatPricing?: ModelSelectorProps["formatPricing"]; + readonly labels: ModelSelectorLabels; + readonly model: ModelInfo; + readonly onSelect: (id: string) => void; + readonly selected: boolean; +}) { + const theme = useTheme(); + const unavailable = model.serviceState?.status === "unavailable"; + return ( + { + onSelect(model.id); + }} + style={({ pressed }) => [ + styles.item, + { + backgroundColor: selected + ? theme.colors.accent + : theme.colors.popover, + borderColor: theme.colors.border, + gap: theme.spacing[1], + paddingHorizontal: theme.spacing[3], + paddingVertical: theme.spacing[2], + }, + pressed ? styles.pressed : undefined, + unavailable ? { opacity: 0.6 } : undefined, + ]} + > + + {model.name} + + + + ); +} +ModelRow.displayName = "ModelRow"; + +type SelectorState = { + readonly changeOpen: (open: boolean) => void; + readonly filteredModels: readonly ModelInfo[]; + readonly handleSelect: (id: string) => void; + readonly isOpen: boolean; + readonly query: string; + readonly selection: string; + readonly setQuery: (query: string) => void; +}; + +function useSelectorState(props: ModelSelectorProps): SelectorState { + const [internalOpen, setInternalOpen] = useState(props.defaultOpen ?? false); + const [internalSelection, setInternalSelection] = useState( + props.defaultSelectedModelId ?? "", + ); + const [query, setQuery] = useState(""); + const openControlled = props.open !== undefined; + const selectionControlled = props.selectedModelId !== undefined; + const isOpen = openControlled ? props.open : internalOpen; + const selection = selectionControlled + ? props.selectedModelId + : internalSelection; + const changeOpen = useCallback( + (next: boolean) => { + if (!openControlled) setInternalOpen(next); + if (!next) setQuery(""); + props.onOpenChange?.(next); + }, + [openControlled, props], + ); + const handleSelect = useCallback( + (modelId: string) => { + if (!selectionControlled) setInternalSelection(modelId); + props.onSelectModel?.(modelId); + changeOpen(false); + }, + [changeOpen, props, selectionControlled], + ); + const filteredModels = useMemo( + () => props.models.filter((model) => modelMatches(model, query)), + [props.models, query], + ); + return { + changeOpen, + filteredModels, + handleSelect, + isOpen, + query, + selection, + setQuery, + }; +} + +function SelectorHeader({ + labels, + onClose, +}: { + readonly labels: ModelSelectorLabels; + readonly onClose: () => void; +}) { + const theme = useTheme(); + return ( + + + + {labels.title} + + + {labels.description} + + + [ + styles.close, + { + borderRadius: theme.radius.md, + paddingHorizontal: theme.spacing[2], + }, + pressed ? styles.pressed : undefined, + ]} + > + + {labels.close} + + + + ); +} +SelectorHeader.displayName = "SelectorHeader"; + +function SelectorList({ + formatPricing, + labels, + models, + onSelect, + selection, +}: { + readonly formatPricing?: ModelSelectorProps["formatPricing"]; + readonly labels: ModelSelectorLabels; + readonly models: readonly ModelInfo[]; + readonly onSelect: (id: string) => void; + readonly selection: string; +}) { + const theme = useTheme(); + return ( + + {models.length === 0 ? ( + + {labels.noModels} + + ) : ( + models.map((model) => ( + + )) + )} + + ); +} +SelectorList.displayName = "SelectorList"; + +function SelectorSearch({ + inputProps, + label, + onChange, + value, +}: { + readonly inputProps?: ModelSelectorProps["searchInputProps"]; + readonly label: string; + readonly onChange: (value: string) => void; + readonly value: string; +}) { + const theme = useTheme(); + const searchId = useId(); + return ( + + ); +} +SelectorSearch.displayName = "SelectorSearch"; + +function SelectorPanel({ + formatPricing, + labels, + reference, + searchInputProps, + state, + style, + viewProps, +}: { + readonly formatPricing?: ModelSelectorProps["formatPricing"]; + readonly labels: ModelSelectorLabels; + readonly reference?: Ref; + readonly searchInputProps?: ModelSelectorProps["searchInputProps"]; + readonly state: SelectorState; + readonly style?: ModelSelectorProps["style"]; + readonly viewProps: ViewProps; +}) { + const theme = useTheme(); + const handleSearchChange = state.setQuery; + return ( + + { + state.changeOpen(false); + }} + /> + + + + ); +} +SelectorPanel.displayName = "SelectorPanel"; + +/** Searchable native modal with controlled or uncontrolled model selection. */ +function ModelSelector({ + defaultOpen, + defaultSelectedModelId, + formatPricing, + labels, + models, + onOpenChange, + onSelectModel, + open, + ref, + searchInputProps, + selectedModelId, + style, + ...viewProps +}: ModelSelectorProps) { + const theme = useTheme(); + const state = useSelectorState({ + defaultOpen, + defaultSelectedModelId, + formatPricing, + labels, + models, + onOpenChange, + onSelectModel, + open, + ref, + searchInputProps, + selectedModelId, + style, + }); + const reducedMotion = useReducedMotion(); + return ( + { + state.changeOpen(false); + }} + transparent + visible={state.isOpen} + > + + + + + ); +} +ModelSelector.displayName = "ModelSelector"; + +export { ModelSelector }; diff --git a/packages/ui-native/src/components/native-ai-components.test.tsx b/packages/ui-native/src/components/native-ai-components.test.tsx new file mode 100644 index 00000000..8aa3fda6 --- /dev/null +++ b/packages/ui-native/src/components/native-ai-components.test.tsx @@ -0,0 +1,290 @@ +import { fireEvent, render, screen } from "@testing-library/react-native"; +import { Text } from "react-native"; + +import { + AgentActivity, + AgentStep, + AgentStepDetail, + AgentStepDetailText, + AgentStepProgress, + AgentStepTitle, +} from "./agent-activity/agent-activity"; +import { AIChatInput } from "./ai-chat-input/ai-chat-input"; +import { ChainOfThought } from "./chain-of-thought/chain-of-thought"; +import { + ConversationEmpty, + ConversationLoading, + ConversationMessages, + ConversationSuggestions, + ConversationThread, +} from "./conversation-thread/conversation-thread"; +import { ModelSelector } from "./model-selector/model-selector"; +import { PromptInput } from "./prompt-input/prompt-input"; +import { Reasoning } from "./reasoning/reasoning"; +import { ThinkingBlock } from "./thinking-block/thinking-block"; + +const thinkingLabels = { + collapse: "Hide thinking", + expand: "Show thinking", + streaming: "Thinking now", + thinking: "Thinking", +}; + +const conversationLabels = { + assistantMessage: "Assistant message", + assistantTyping: "Assistant is typing", + negativeFeedback: "Not helpful", + positiveFeedback: "Helpful", + retry: "Try again", + scrollToBottom: "Read newest message", + toolCalls: "Tools used", + userMessage: "Your message", +}; + +const activityLabels = { + activity: "Agent activity", + collapse: "Hide details", + elapsed: "Elapsed time", + expand: "Show details", + status: { + completed: "Completed", + error: "Failed", + idle: "Idle", + pending: "Pending", + running: "Running", + skipped: "Skipped", + unavailable: "Service unavailable", + }, +}; + +describe("native AI components", () => { + it("submits uncontrolled chat and prompt values with native actions", () => { + const onChatSubmit = jest.fn(); + const onPromptSubmit = jest.fn(); + render( + <> + + + , + ); + + fireEvent.changeText(screen.getByLabelText("Chat message"), "Hello"); + fireEvent.press(screen.getByRole("button", { name: "Send chat" })); + fireEvent.changeText(screen.getByLabelText("Prompt"), "Plan this"); + fireEvent(screen.getByLabelText("Prompt"), "submitEditing"); + + expect(screen.getByTestId("chat-composer")).toBeOnTheScreen(); + expect(screen.getByTestId("prompt-composer")).toBeOnTheScreen(); + expect(onChatSubmit).toHaveBeenCalledWith("Hello"); + expect(onPromptSubmit).toHaveBeenCalledWith("Plan this"); + expect(screen.getByLabelText("Chat message")).toHaveProp("value", ""); + expect(screen.getByLabelText("Prompt")).toHaveProp("value", ""); + }); + + it("exposes unavailable services and blocks unavailable actions", () => { + const onChatSubmit = jest.fn(); + const onPromptSubmit = jest.fn(); + const onSelectModel = jest.fn(); + render( + <> + + + + , + ); + + expect(screen.getByRole("button", { name: "Send chat" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Send prompt" })).toBeDisabled(); + expect(screen.getByRole("radio", { name: "Offline model" })).toBeDisabled(); + + expect(screen.getByTestId("model-selector")).toBeOnTheScreen(); + expect(screen.getByText("Chat is offline")).toBeOnTheScreen(); + expect(screen.getByText("Prompts are offline")).toBeOnTheScreen(); + expect( + screen.getByText("Unavailable: Provider maintenance"), + ).toBeOnTheScreen(); + expect(onChatSubmit).not.toHaveBeenCalled(); + expect(onPromptSubmit).not.toHaveBeenCalled(); + expect(onSelectModel).not.toHaveBeenCalled(); + }); + + it("renders stable ordered reasoning and controlled disclosures", () => { + const onReasoningOpenChange = jest.fn(); + const onThinkingExpandedChange = jest.fn(); + render( + <> + + + + , + ); + + expect(screen.getByLabelText("Read request, Complete")).toBeOnTheScreen(); + expect(screen.getByLabelText("Draft answer, Active")).toBeBusy(); + fireEvent.press(screen.getByRole("button", { name: "Show reasoning" })); + fireEvent.press(screen.getByRole("button", { name: "Show thinking" })); + expect(onReasoningOpenChange).toHaveBeenCalledWith(true); + expect(onThinkingExpandedChange).toHaveBeenCalledWith(true); + expect(screen.queryByText("Check constraints")).not.toBeOnTheScreen(); + expect(screen.queryByText("Private trace")).not.toBeOnTheScreen(); + }); + + it("renders conversation and agent activity compound parts", () => { + const onSend = jest.fn(); + render( + <> + + + + + + + + + + + + + + + + + Call service + + + Waiting for response + + + + , + ); + + expect(screen.getByText("Assistant is typing")).toBeOnTheScreen(); + expect(screen.getByLabelText("Service progress")).toHaveAccessibilityValue({ + max: 100, + min: 0, + now: 50, + }); + fireEvent.press(screen.getByRole("button", { name: "Say hello" })); + expect(onSend).toHaveBeenCalledWith("Hello"); + expect(screen.getByText("Waiting for response")).toBeOnTheScreen(); + }); + + it("keeps caller content as native nodes without claiming rich text", () => { + render( + + Plain native content + , + ); + + expect(screen.getByText("Plain native content")).toBeOnTheScreen(); + }); +}); diff --git a/packages/ui-native/src/components/prompt-input/prompt-input.tsx b/packages/ui-native/src/components/prompt-input/prompt-input.tsx new file mode 100644 index 00000000..2cf5330d --- /dev/null +++ b/packages/ui-native/src/components/prompt-input/prompt-input.tsx @@ -0,0 +1,370 @@ +"use client"; + +import { type ReactNode, type Ref, useCallback, useId, useState } from "react"; + +import { + Pressable, + StyleSheet, + Text, + TextInput, + type TextInputContentSizeChangeEvent, + type TextInputProps, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Explicit availability of the service receiving a prompt. */ +export type PromptServiceState = + | { readonly message: string; readonly status: "unavailable" } + | { readonly status: "available" }; + +/** Native multiline return-key behavior. */ +export type PromptSubmitBehavior = "newline" | "submit"; + +/** Props for the compact native prompt composer. */ +export type PromptInputProps = Omit & { + readonly defaultValue?: string; + readonly disabled?: boolean; + readonly inputLabel: string; + readonly inputProps?: Omit< + TextInputProps, + | "editable" + | "multiline" + | "onChangeText" + | "onContentSizeChange" + | "onSubmitEditing" + | "submitBehavior" + | "value" + >; + readonly isLoading?: boolean; + readonly maxRows?: number; + readonly minRows?: number; + readonly onSubmit?: (value: string) => void; + readonly onValueChange?: (value: string) => void; + readonly ref?: Ref; + readonly serviceState?: PromptServiceState; + readonly submitBehavior?: PromptSubmitBehavior; + readonly submitLabel: string; + readonly toolbar?: ReactNode; + readonly value?: string; +}; + +const styles = StyleSheet.create({ + actions: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + }, + input: { padding: 0, textAlignVertical: "top" }, + pressed: { opacity: 0.8 }, + root: { borderWidth: 1 }, + submit: { + alignItems: "center", + justifyContent: "center", + minHeight: 44, + minWidth: 44, + }, + toolbar: { + alignItems: "center", + flex: 1, + flexDirection: "row", + flexWrap: "wrap", + }, +}); + +type PromptState = { + readonly canSubmit: boolean; + readonly contentHeight: number; + readonly handleContentSizeChange: ( + event: TextInputContentSizeChangeEvent, + ) => void; + readonly handleSubmit: () => void; + readonly handleValueChange: (value: string) => void; + readonly maxHeight: number; + readonly unavailable: boolean; + readonly value: string; +}; + +function usePromptState(props: PromptInputProps): PromptState { + const theme = useTheme(); + const minimum = Math.max(1, props.minRows ?? 1); + const maximum = Math.max(minimum, props.maxRows ?? 8); + const rowHeight = theme.typography.scale.bodySmall.lineHeight; + const minHeight = minimum * rowHeight; + const maxHeight = maximum * rowHeight; + const [contentHeight, setContentHeight] = useState(minHeight); + const [internalValue, setInternalValue] = useState(props.defaultValue ?? ""); + const controlled = props.value !== undefined; + const value = controlled ? props.value : internalValue; + const unavailable = props.serviceState?.status === "unavailable"; + const canSubmit = + props.disabled !== true && + props.isLoading !== true && + !unavailable && + value.trim().length > 0; + const handleValueChange = useCallback( + (next: string) => { + if (!controlled) setInternalValue(next); + props.onValueChange?.(next); + }, + [controlled, props], + ); + const handleSubmit = useCallback(() => { + if (!canSubmit) return; + props.onSubmit?.(value); + if (!controlled) setInternalValue(""); + }, [canSubmit, controlled, props, value]); + const handleContentSizeChange = useCallback( + (event: TextInputContentSizeChangeEvent) => { + const next = event.nativeEvent.contentSize.height; + setContentHeight(Math.min(maxHeight, Math.max(minHeight, next))); + }, + [maxHeight, minHeight], + ); + return { + canSubmit, + contentHeight, + handleContentSizeChange, + handleSubmit, + handleValueChange, + maxHeight, + unavailable, + value, + }; +} + +function PromptField({ + disabled, + inputLabel, + inputProps, + state, + submitBehavior, +}: { + readonly disabled: boolean; + readonly inputLabel: string; + readonly inputProps?: PromptInputProps["inputProps"]; + readonly state: PromptState; + readonly submitBehavior: PromptSubmitBehavior; +}) { + const theme = useTheme(); + const inputId = useId(); + return ( + = state.maxHeight} + style={[ + styles.input, + theme.typography.scale.bodySmall, + { color: theme.colors.foreground, height: state.contentHeight }, + inputProps?.style, + ]} + submitBehavior={submitBehavior} + value={state.value} + /> + ); +} +PromptField.displayName = "PromptField"; + +function PromptAction({ + canSubmit, + isLoading, + label, + onPress, +}: { + readonly canSubmit: boolean; + readonly isLoading: boolean; + readonly label: string; + readonly onPress: () => void; +}) { + const theme = useTheme(); + return ( + [ + styles.submit, + { + backgroundColor: theme.colors.primary, + borderRadius: theme.radius.md, + opacity: canSubmit ? 1 : 0.5, + paddingHorizontal: theme.spacing[3], + }, + pressed ? styles.pressed : undefined, + ]} + > + + {label} + + + ); +} +PromptAction.displayName = "PromptAction"; + +function PromptShell({ + children, + reference, + style, + viewProps, +}: { + readonly children: ReactNode; + readonly reference?: Ref; + readonly style?: PromptInputProps["style"]; + readonly viewProps: ViewProps; +}) { + const theme = useTheme(); + return ( + + {children} + + ); +} +PromptShell.displayName = "PromptShell"; + +function PromptFooter({ + isLoading, + state, + submitLabel, + toolbar, + unavailableMessage, +}: { + readonly isLoading: boolean; + readonly state: PromptState; + readonly submitLabel: string; + readonly toolbar?: ReactNode; + readonly unavailableMessage?: string; +}) { + const theme = useTheme(); + return ( + <> + {unavailableMessage ? ( + + {unavailableMessage} + + ) : null} + + + {toolbar} + + + + + ); +} +PromptFooter.displayName = "PromptFooter"; + +/** Auto-growing native prompt composer with explicit return-key behavior. */ +function PromptInput({ + defaultValue, + disabled = false, + inputLabel, + inputProps, + isLoading = false, + maxRows, + minRows, + onSubmit, + onValueChange, + ref, + serviceState, + style, + submitBehavior = "submit", + submitLabel, + toolbar, + value, + ...viewProps +}: PromptInputProps) { + const stateProps = { + defaultValue, + disabled, + inputLabel, + inputProps, + isLoading, + maxRows, + minRows, + onSubmit, + onValueChange, + serviceState, + submitBehavior, + submitLabel, + toolbar, + value, + }; + const state = usePromptState({ + ...stateProps, + inputLabel, + inputProps, + isLoading, + serviceState, + submitBehavior, + submitLabel, + toolbar, + }); + const unavailableMessage = + serviceState?.status === "unavailable" ? serviceState.message : undefined; + return ( + + + + + ); +} +PromptInput.displayName = "PromptInput"; + +export { PromptInput }; diff --git a/packages/ui-native/src/components/reasoning/reasoning.tsx b/packages/ui-native/src/components/reasoning/reasoning.tsx new file mode 100644 index 00000000..54c4d3aa --- /dev/null +++ b/packages/ui-native/src/components/reasoning/reasoning.tsx @@ -0,0 +1,212 @@ +"use client"; + +import { type ReactNode, type Ref, useCallback, useId, useState } from "react"; + +import { + Pressable, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** One stable text reasoning item. */ +export type ReasoningStep = { + readonly id: string; + readonly text: string; +}; + +/** Caller-localized copy for the native reasoning disclosure. */ +export type ReasoningLabels = { + readonly collapse: string; + readonly expand: string; + readonly reasoned: string; + readonly reasoning: string; +}; + +/** Props for the native reasoning disclosure. */ +export type ReasoningProps = Omit & { + readonly children?: ReactNode; + readonly defaultOpen?: boolean; + readonly duration?: ReactNode; + readonly isStreaming?: boolean; + readonly labels: ReasoningLabels; + readonly onOpenChange?: (open: boolean) => void; + readonly open?: boolean; + readonly ref?: Ref; + readonly steps?: readonly ReasoningStep[]; +}; + +const styles = StyleSheet.create({ + content: { borderTopWidth: 1 }, + pressed: { opacity: 0.8 }, + root: { borderWidth: 1, overflow: "hidden" }, + step: { flexDirection: "row" }, + trigger: { + alignItems: "center", + flexDirection: "row", + minHeight: 44, + }, + triggerLabel: { flex: 1 }, +}); + +type TriggerProps = { + readonly duration?: ReactNode; + readonly isOpen: boolean; + readonly isStreaming: boolean; + readonly labels: ReasoningLabels; + readonly onPress: () => void; +}; + +function ReasoningTrigger({ + duration, + isOpen, + isStreaming, + labels, + onPress, +}: TriggerProps) { + const theme = useTheme(); + return ( + [ + styles.trigger, + { gap: theme.spacing[2], paddingHorizontal: theme.spacing[3] }, + pressed ? styles.pressed : undefined, + ]} + > + + {isStreaming ? labels.reasoning : labels.reasoned} + + {duration} + + ); +} +ReasoningTrigger.displayName = "ReasoningTrigger"; + +function ReasoningContent({ + children, + contentId, + steps, +}: { + readonly children?: ReactNode; + readonly contentId: string; + readonly steps?: readonly ReasoningStep[]; +}) { + const theme = useTheme(); + return ( + + {steps && steps.length > 0 + ? steps.map((step, index) => ( + + + {index + 1}. + + + {step.text} + + + )) + : children} + + ); +} +ReasoningContent.displayName = "ReasoningContent"; + +/** Collapsible text reasoning trace with controlled and uncontrolled state. */ +function Reasoning({ + children, + defaultOpen = false, + duration, + isStreaming = false, + labels, + onOpenChange, + open, + ref, + steps, + style, + ...props +}: ReasoningProps) { + const theme = useTheme(); + const [internalOpen, setInternalOpen] = useState(defaultOpen); + const controlled = open !== undefined; + const isOpen = isStreaming || (controlled ? open : internalOpen); + const contentId = useId(); + const handleToggle = useCallback(() => { + const next = !isOpen; + if (!controlled) setInternalOpen(next); + onOpenChange?.(next); + }, [controlled, isOpen, onOpenChange]); + + return ( + + + {isOpen ? ( + + {children} + + ) : null} + + ); +} +Reasoning.displayName = "Reasoning"; + +export { Reasoning }; diff --git a/packages/ui-native/src/components/thinking-block/thinking-block.tsx b/packages/ui-native/src/components/thinking-block/thinking-block.tsx new file mode 100644 index 00000000..42a9ef45 --- /dev/null +++ b/packages/ui-native/src/components/thinking-block/thinking-block.tsx @@ -0,0 +1,142 @@ +"use client"; + +import { type Ref, useCallback, useId, useState } from "react"; + +import { + Pressable, + StyleSheet, + Text, + View, + type ViewProps, +} from "react-native"; + +import { useTheme } from "../../theme/theme-provider"; + +/** Caller-localized copy for the native thinking disclosure. */ +export type ThinkingBlockLabels = { + readonly collapse: string; + readonly expand: string; + readonly streaming: string; + readonly thinking: string; +}; + +/** Props for a text native thinking disclosure. */ +export type ThinkingBlockProps = Omit & { + readonly defaultExpanded?: boolean; + readonly expanded?: boolean; + readonly isStreaming?: boolean; + readonly labels: ThinkingBlockLabels; + readonly onExpandedChange?: (expanded: boolean) => void; + readonly ref?: Ref; + readonly thinking: string; +}; + +const styles = StyleSheet.create({ + content: { borderLeftWidth: 1 }, + pressed: { opacity: 0.8 }, + trigger: { + alignItems: "center", + flexDirection: "row", + minHeight: 44, + }, +}); + +function ThinkingContent({ + contentId, + isStreaming, + thinking, +}: { + readonly contentId: string; + readonly isStreaming: boolean; + readonly thinking: string; +}) { + const theme = useTheme(); + return ( + + + {thinking} + + + ); +} +ThinkingContent.displayName = "ThinkingContent"; + +/** Collapsible text thinking trace for React Native. */ +function ThinkingBlock({ + defaultExpanded = false, + expanded, + isStreaming = false, + labels, + onExpandedChange, + ref, + style, + thinking, + ...props +}: ThinkingBlockProps) { + const theme = useTheme(); + const [internalExpanded, setInternalExpanded] = useState(defaultExpanded); + const controlled = expanded !== undefined; + const isExpanded = isStreaming || (controlled ? expanded : internalExpanded); + const contentId = useId(); + const handleToggle = useCallback(() => { + const next = !isExpanded; + if (!controlled) setInternalExpanded(next); + onExpandedChange?.(next); + }, [controlled, isExpanded, onExpandedChange]); + + return ( + + [ + styles.trigger, + { gap: theme.spacing[2] }, + pressed ? styles.pressed : undefined, + ]} + > + + {isStreaming ? labels.streaming : labels.thinking} + + + {isExpanded ? ( + + ) : null} + + ); +} +ThinkingBlock.displayName = "ThinkingBlock"; + +export { ThinkingBlock }; From 36eac751365030bc9b0803886d67caedc768cadf Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Fri, 4 Sep 2026 02:26:39 +0200 Subject: [PATCH 06/18] feat(native): expand cross-platform source catalog --- CHANGELOG.md | 2 +- CONTRIBUTING.md | 19 + README.md | 8 +- ROADMAP.md | 19 +- apps/native-catalog/App.test.tsx | 16 +- apps/native-catalog/App.tsx | 24 +- apps/native-catalog/README.md | 9 +- apps/native-catalog/catalog-sections.tsx | 245 +- apps/native-catalog/jest.config.cjs | 3 + .../app/[locale]/build/[slug]/page.tsx | 5 +- apps/registry/app/[locale]/changelog/page.tsx | 5 +- .../app/[locale]/components/[slug]/page.tsx | 365 ++- .../components/[slug]/playground/page.tsx | 5 +- .../registry/app/[locale]/components/page.tsx | 68 +- apps/registry/app/[locale]/design/page.tsx | 5 +- .../app/[locale]/docs/[slug]/page.tsx | 5 +- apps/registry/app/[locale]/docs/page.tsx | 5 +- .../app/[locale]/families/[category]/page.tsx | 88 +- apps/registry/app/[locale]/families/page.tsx | 64 +- apps/registry/app/[locale]/layout.tsx | 6 +- apps/registry/app/[locale]/native/page.tsx | 173 ++ apps/registry/app/[locale]/not-found.tsx | 4 +- apps/registry/app/[locale]/page.tsx | 4 +- .../registry/app/[locale]/philosophy/page.tsx | 5 +- apps/registry/app/[locale]/releases/page.tsx | 5 +- apps/registry/app/[locale]/report/page.tsx | 4 +- .../app/[locale]/request-component/page.tsx | 4 +- .../app/[locale]/templates/[slug]/page.tsx | 5 +- apps/registry/app/[locale]/templates/page.tsx | 5 +- apps/registry/app/[locale]/themes/page.tsx | 4 +- .../app/[locale]/vs/assistant-ui/page.tsx | 4 +- apps/registry/app/[locale]/vs/page.tsx | 4 +- apps/registry/app/[locale]/vs/shadcn/page.tsx | 4 +- .../app/[locale]/vs/vercel-ai-sdk/page.tsx | 4 +- apps/registry/app/llms-full.txt/route.ts | 20 +- apps/registry/app/llms.txt/route.ts | 25 +- apps/registry/app/manifest.ts | 3 +- apps/registry/app/mcp/route.test.ts | 60 +- apps/registry/app/mcp/route.ts | 172 +- .../app/r/native/registry.json/route.ts | 10 + apps/registry/app/sitemap.ts | 36 +- .../component-card/component-card.tsx | 49 +- apps/registry/components/footer/footer.tsx | 6 + apps/registry/components/header/header.tsx | 229 +- apps/registry/components/landing/landing.tsx | 87 +- .../components/platform-selector/index.ts | 1 + .../platform-selector/platform-selector.tsx | 72 + .../components/platform-sidebar/index.ts | 1 + .../platform-sidebar/platform-sidebar.tsx | 58 + .../components/quick-add/quick-add.tsx | 6 +- .../registry/content/pages/docs/native/en.mdx | 67 +- .../registry/content/pages/docs/native/fr.mdx | 67 +- apps/registry/e2e/platforms.spec.ts | 160 +- apps/registry/lib/component-metadata.json | 498 ++-- apps/registry/lib/jsonld.test.ts | 22 +- apps/registry/lib/jsonld.ts | 16 +- apps/registry/lib/native-registry.test.ts | 38 + apps/registry/lib/native-registry.ts | 42 + apps/registry/lib/platform.test.ts | 38 + apps/registry/lib/platform.ts | 71 + apps/registry/lib/registry.test.ts | 8 +- apps/registry/lib/registry.ts | 4 +- apps/registry/lib/sidebar-sections.ts | 1 + apps/registry/messages/en.json | 83 +- apps/registry/messages/fr.json | 83 +- apps/registry/package.json | 2 +- apps/registry/registry.json | 2202 ++++++++++++++--- .../default/animated-text/animated-text.tsx | 15 +- .../interactive-timeline.tsx | 38 +- .../default/map-timeline/map-timeline.tsx | 15 +- .../registry/default/sidebar/sidebar.tsx | 14 +- .../default/typewriter/typewriter.tsx | 14 +- .../scripts/check-registry-integrity.ts | 46 +- .../scripts/inline-component-source.ts | 18 +- .../scripts/stamp-registry-metadata.ts | 4 +- docs/ARCHITECTURE.md | 14 +- docs/CROSS_PLATFORM_CATALOG_PLAN.md | 336 +++ docs/RELEASING.md | 6 +- package.json | 2 +- packages/ui-native/CHANGELOG.md | 10 +- packages/ui-native/README.md | 61 +- packages/ui-native/eslint.config.js | 103 + packages/ui-native/jest.config.cjs | 1 + packages/ui-native/jest.setup.cjs | 7 + packages/ui-native/package.json | 2 + packages/ui-native/registry.json | 873 ++++++- packages/ui-native/registry.schema.json | 61 +- packages/ui-native/scripts/generate-index.mjs | 62 + .../components/activity-log/activity-log.tsx | 242 ++ .../components/advanced-forms-native.test.tsx | 197 ++ .../components/ai-artifact/ai-artifact.tsx | 496 ++++ .../ai-message-bubble/ai-message-bubble.tsx | 171 ++ .../ai-source-citation/ai-source-citation.tsx | 94 + .../ai-streaming-text/ai-streaming-text.tsx | 58 + .../ai-tool-call-display.tsx | 188 ++ .../components/alert-dialog/alert-dialog.tsx | 206 ++ .../ui-native/src/components/alert/alert.tsx | 112 + .../animated-tabs/animated-tabs.tsx | 206 ++ .../animation-utilities-native.test.tsx | 21 + .../components/aspect-ratio/aspect-ratio.tsx | 27 + .../components/avatar-group/avatar-group.tsx | 168 ++ .../src/components/avatar/avatar.tsx | 114 + .../src/components/banner/banner.tsx | 159 ++ .../src/components/bottom-bar/bottom-bar.tsx | 66 + .../src/components/breadcrumb/breadcrumb.tsx | 130 + .../components/button-group/button-group.tsx | 48 + .../src/components/calendar/calendar.tsx | 233 ++ .../src/components/callout/callout.tsx | 114 + .../category-filter/category-filter.tsx | 25 + .../checkbox-group/checkbox-group.tsx | 126 + .../src/components/checkbox/checkbox.tsx | 118 + .../src/components/checklist/checklist.tsx | 274 ++ .../components/color-picker/color-picker.tsx | 102 + .../src/components/combobox/combobox.tsx | 259 ++ .../src/components/command/command.tsx | 253 ++ .../completion-dialog/completion-dialog.tsx | 211 ++ .../content-ai-utility-native.test.tsx | 204 ++ .../content-intro/content-intro.tsx | 289 +++ .../components/context-menu/context-menu.tsx | 157 ++ .../conversation-thread.tsx | 12 +- .../components/copy-button/copy-button.tsx | 184 ++ .../components/core-controls-native.test.tsx | 283 +++ .../countdown-timer/countdown-timer.tsx | 273 ++ .../components/credit-badge/credit-badge.tsx | 63 + .../src/components/data-list/data-list.tsx | 121 + .../src/components/data-native.test.tsx | 283 +++ .../src/components/date-field/date-field.tsx | 116 + .../components/date-picker/date-picker.tsx | 139 ++ .../date-range-picker/date-range-picker.tsx | 139 ++ .../src/components/dialog/dialog.tsx | 176 ++ .../src/components/drawer/drawer.tsx | 152 ++ .../dropdown-menu/dropdown-menu.tsx | 187 ++ .../components/empty-state/empty-state.tsx | 151 ++ .../src/components/exercise/exercise.tsx | 290 +++ .../ui-native/src/components/field/field.tsx | 149 ++ .../src/components/fieldset/fieldset.tsx | 83 + .../components/file-upload/file-upload.tsx | 193 ++ .../src/components/filter-bar/filter-bar.tsx | 41 + .../src/components/flashcard/flashcard.tsx | 199 ++ .../floating-action-button.tsx | 76 + .../ui-native/src/components/form/form.tsx | 130 + .../foundation-form-native.test.tsx | 267 ++ .../glass-progress/glass-progress.tsx | 72 + .../ui-native/src/components/grid/grid.tsx | 49 + .../horizontal-scroll-row.tsx | 79 + .../components/inline-input/inline-input.tsx | 59 + .../components/input-group/input-group.tsx | 82 + .../src/components/input-otp/input-otp.tsx | 99 + .../ui-native/src/components/input/input.tsx | 70 + .../ui-native/src/components/item/item.tsx | 137 + .../keyboard-shortcuts-help.tsx | 236 ++ .../ui-native/src/components/label/label.tsx | 44 + .../learning-composites-native.test.tsx | 416 ++++ .../ui-native/src/components/link/link.tsx | 124 + .../src/components/list-box/list-box.tsx | 123 + .../src/components/live-feed/live-feed.tsx | 218 ++ .../src/components/menubar/menubar.tsx | 206 ++ .../ui-native/src/components/meter/meter.tsx | 102 + .../metric-cluster/metric-cluster.tsx | 149 ++ .../model-selector/model-selector.tsx | 67 +- .../components/multi-select/multi-select.tsx | 247 ++ .../components/native-ai-components.test.tsx | 42 + .../native-select/native-select.tsx | 25 + .../navigation-menu/navigation-menu.tsx | 173 ++ .../src/components/navigation-native.test.tsx | 335 +++ .../components/number-input/number-input.tsx | 254 ++ .../number-ticker/number-ticker.tsx | 88 +- .../src/components/overlays-native.test.tsx | 301 +++ .../overview-board/overview-board.tsx | 168 ++ .../src/components/pagination/pagination.tsx | 159 ++ .../ui-native/src/components/panel/panel.tsx | 165 ++ .../password-input/password-input.tsx | 86 + .../components/phone-input/phone-input.tsx | 110 + .../src/components/plan-badge/plan-badge.tsx | 69 + .../src/components/popover/popover.tsx | 136 + .../presence-stack/presence-stack.tsx | 203 ++ .../presence-sync-indicator.tsx | 133 + .../components/progress-bar/progress-bar.tsx | 190 ++ .../progress-card/progress-card.tsx | 160 ++ .../progress-tracker/progress-tracker.tsx | 487 ++++ .../ui-native/src/components/quiz/quiz.tsx | 335 +++ .../components/radio-group/radio-group.tsx | 215 ++ .../range-calendar/range-calendar.tsx | 54 + .../src/components/rating/rating.tsx | 174 ++ .../src/components/resizable/resizable.tsx | 32 +- .../src/components/role-badge/role-badge.tsx | 60 + .../src/components/search-bar/search-bar.tsx | 88 + .../search-dialog/search-dialog.tsx | 583 +++++ .../components/search-field/search-field.tsx | 94 + .../segmented-control/segmented-control.tsx | 113 + .../src/components/select/select.tsx | 240 ++ .../src/components/separator/separator.tsx | 43 + .../severity-badge/severity-badge.tsx | 166 ++ .../components/share-dialog/share-dialog.tsx | 208 ++ .../ui-native/src/components/sheet/sheet.tsx | 165 ++ .../sidebar-provider/sidebar-provider.tsx | 88 + .../sidebar-toggle/sidebar-toggle.tsx | 76 + .../src/components/sidebar/sidebar.tsx | 143 ++ .../src/components/skeleton/skeleton.tsx | 38 + .../src/components/slider/slider.tsx | 187 ++ .../src/components/spinner/spinner.tsx | 49 + .../src/components/stat-card/stat-card.tsx | 156 ++ .../components/status-board/status-board.tsx | 230 ++ .../status-indicator/status-indicator.tsx | 191 ++ .../components/step-by-step/step-by-step.tsx | 265 ++ .../step-navigation/step-navigation.tsx | 136 + .../src/components/stepper/stepper.tsx | 241 ++ .../sticky-metric/sticky-metric.tsx | 112 + .../src/components/switch/switch.tsx | 49 + .../ui-native/src/components/tabs/tabs.tsx | 237 ++ .../src/components/tag-group/tag-group.tsx | 161 ++ .../src/components/tags-input/tags-input.tsx | 160 ++ .../src/components/text-field/text-field.tsx | 89 + .../src/components/textarea/textarea.tsx | 21 + .../src/components/time-field/time-field.tsx | 111 + .../components/time-picker/time-picker.tsx | 207 ++ .../timeline-scrubber/timeline-scrubber.tsx | 162 ++ .../components/tldr-section/tldr-section.tsx | 128 + .../ui-native/src/components/toast/toast.tsx | 250 ++ .../components/toggle-group/toggle-group.tsx | 251 ++ .../src/components/toggle/toggle.tsx | 118 + .../src/components/toolbar/toolbar.tsx | 142 ++ .../src/components/tooltip/tooltip.tsx | 156 ++ .../src/components/top-bar/top-bar.tsx | 86 + .../ui-native/src/components/tour/tour.tsx | 307 +++ .../truncated-text/truncated-text.tsx | 49 + .../tutorial-complete/tutorial-complete.tsx | 324 +++ .../tutorial-filters/tutorial-filters.tsx | 310 +++ .../view-switcher/view-switcher.tsx | 150 ++ .../workspace-switcher/workspace-switcher.tsx | 145 ++ .../world-clock-bar/world-clock-bar.tsx | 229 ++ packages/ui-native/src/index.ts | 211 +- .../interaction-core-task20.test.tsx | 255 ++ .../ui-native/src/primitives/modal-layer.tsx | 93 + .../src/primitives/platform-services.ts | 108 + .../ui-native/src/primitives/selection.ts | 79 + .../src/primitives/use-controllable-state.ts | 65 + .../src/primitives/use-reduced-motion.ts | 63 + packages/ui/eslint.config.js | 3 +- packages/ui/package.json | 3 +- packages/ui/scripts/check-packed-package.mjs | 256 ++ .../animated-text/animated-text.tsx | 15 +- .../interactive-timeline.tsx | 38 +- .../components/map-timeline/map-timeline.tsx | 15 +- .../ui/src/components/sidebar/sidebar.tsx | 14 +- .../src/components/typewriter/typewriter.tsx | 14 +- packages/ui/src/lib/use-horizontal-scroll.ts | 49 +- packages/ui/src/test-setup.ts | 26 + packages/ui/tsup.config.ts | 111 +- pnpm-lock.yaml | 6 +- 250 files changed, 30137 insertions(+), 1383 deletions(-) create mode 100644 apps/registry/app/[locale]/native/page.tsx create mode 100644 apps/registry/app/r/native/registry.json/route.ts create mode 100644 apps/registry/components/platform-selector/index.ts create mode 100644 apps/registry/components/platform-selector/platform-selector.tsx create mode 100644 apps/registry/components/platform-sidebar/index.ts create mode 100644 apps/registry/components/platform-sidebar/platform-sidebar.tsx create mode 100644 apps/registry/lib/native-registry.test.ts create mode 100644 apps/registry/lib/native-registry.ts create mode 100644 apps/registry/lib/platform.test.ts create mode 100644 apps/registry/lib/platform.ts create mode 100644 docs/CROSS_PLATFORM_CATALOG_PLAN.md create mode 100644 packages/ui-native/jest.setup.cjs create mode 100644 packages/ui-native/scripts/generate-index.mjs create mode 100644 packages/ui-native/src/components/activity-log/activity-log.tsx create mode 100644 packages/ui-native/src/components/advanced-forms-native.test.tsx create mode 100644 packages/ui-native/src/components/ai-artifact/ai-artifact.tsx create mode 100644 packages/ui-native/src/components/ai-message-bubble/ai-message-bubble.tsx create mode 100644 packages/ui-native/src/components/ai-source-citation/ai-source-citation.tsx create mode 100644 packages/ui-native/src/components/ai-streaming-text/ai-streaming-text.tsx create mode 100644 packages/ui-native/src/components/ai-tool-call-display/ai-tool-call-display.tsx create mode 100644 packages/ui-native/src/components/alert-dialog/alert-dialog.tsx create mode 100644 packages/ui-native/src/components/alert/alert.tsx create mode 100644 packages/ui-native/src/components/animated-tabs/animated-tabs.tsx create mode 100644 packages/ui-native/src/components/aspect-ratio/aspect-ratio.tsx create mode 100644 packages/ui-native/src/components/avatar-group/avatar-group.tsx create mode 100644 packages/ui-native/src/components/avatar/avatar.tsx create mode 100644 packages/ui-native/src/components/banner/banner.tsx create mode 100644 packages/ui-native/src/components/bottom-bar/bottom-bar.tsx create mode 100644 packages/ui-native/src/components/breadcrumb/breadcrumb.tsx create mode 100644 packages/ui-native/src/components/button-group/button-group.tsx create mode 100644 packages/ui-native/src/components/calendar/calendar.tsx create mode 100644 packages/ui-native/src/components/callout/callout.tsx create mode 100644 packages/ui-native/src/components/category-filter/category-filter.tsx create mode 100644 packages/ui-native/src/components/checkbox-group/checkbox-group.tsx create mode 100644 packages/ui-native/src/components/checkbox/checkbox.tsx create mode 100644 packages/ui-native/src/components/checklist/checklist.tsx create mode 100644 packages/ui-native/src/components/color-picker/color-picker.tsx create mode 100644 packages/ui-native/src/components/combobox/combobox.tsx create mode 100644 packages/ui-native/src/components/command/command.tsx create mode 100644 packages/ui-native/src/components/completion-dialog/completion-dialog.tsx create mode 100644 packages/ui-native/src/components/content-ai-utility-native.test.tsx create mode 100644 packages/ui-native/src/components/content-intro/content-intro.tsx create mode 100644 packages/ui-native/src/components/context-menu/context-menu.tsx create mode 100644 packages/ui-native/src/components/copy-button/copy-button.tsx create mode 100644 packages/ui-native/src/components/core-controls-native.test.tsx create mode 100644 packages/ui-native/src/components/countdown-timer/countdown-timer.tsx create mode 100644 packages/ui-native/src/components/credit-badge/credit-badge.tsx create mode 100644 packages/ui-native/src/components/data-list/data-list.tsx create mode 100644 packages/ui-native/src/components/data-native.test.tsx create mode 100644 packages/ui-native/src/components/date-field/date-field.tsx create mode 100644 packages/ui-native/src/components/date-picker/date-picker.tsx create mode 100644 packages/ui-native/src/components/date-range-picker/date-range-picker.tsx create mode 100644 packages/ui-native/src/components/dialog/dialog.tsx create mode 100644 packages/ui-native/src/components/drawer/drawer.tsx create mode 100644 packages/ui-native/src/components/dropdown-menu/dropdown-menu.tsx create mode 100644 packages/ui-native/src/components/empty-state/empty-state.tsx create mode 100644 packages/ui-native/src/components/exercise/exercise.tsx create mode 100644 packages/ui-native/src/components/field/field.tsx create mode 100644 packages/ui-native/src/components/fieldset/fieldset.tsx create mode 100644 packages/ui-native/src/components/file-upload/file-upload.tsx create mode 100644 packages/ui-native/src/components/filter-bar/filter-bar.tsx create mode 100644 packages/ui-native/src/components/flashcard/flashcard.tsx create mode 100644 packages/ui-native/src/components/floating-action-button/floating-action-button.tsx create mode 100644 packages/ui-native/src/components/form/form.tsx create mode 100644 packages/ui-native/src/components/foundation-form-native.test.tsx create mode 100644 packages/ui-native/src/components/glass-progress/glass-progress.tsx create mode 100644 packages/ui-native/src/components/grid/grid.tsx create mode 100644 packages/ui-native/src/components/horizontal-scroll-row/horizontal-scroll-row.tsx create mode 100644 packages/ui-native/src/components/inline-input/inline-input.tsx create mode 100644 packages/ui-native/src/components/input-group/input-group.tsx create mode 100644 packages/ui-native/src/components/input-otp/input-otp.tsx create mode 100644 packages/ui-native/src/components/input/input.tsx create mode 100644 packages/ui-native/src/components/item/item.tsx create mode 100644 packages/ui-native/src/components/keyboard-shortcuts-help/keyboard-shortcuts-help.tsx create mode 100644 packages/ui-native/src/components/label/label.tsx create mode 100644 packages/ui-native/src/components/learning-composites-native.test.tsx create mode 100644 packages/ui-native/src/components/link/link.tsx create mode 100644 packages/ui-native/src/components/list-box/list-box.tsx create mode 100644 packages/ui-native/src/components/live-feed/live-feed.tsx create mode 100644 packages/ui-native/src/components/menubar/menubar.tsx create mode 100644 packages/ui-native/src/components/meter/meter.tsx create mode 100644 packages/ui-native/src/components/metric-cluster/metric-cluster.tsx create mode 100644 packages/ui-native/src/components/multi-select/multi-select.tsx create mode 100644 packages/ui-native/src/components/native-select/native-select.tsx create mode 100644 packages/ui-native/src/components/navigation-menu/navigation-menu.tsx create mode 100644 packages/ui-native/src/components/navigation-native.test.tsx create mode 100644 packages/ui-native/src/components/number-input/number-input.tsx create mode 100644 packages/ui-native/src/components/overlays-native.test.tsx create mode 100644 packages/ui-native/src/components/overview-board/overview-board.tsx create mode 100644 packages/ui-native/src/components/pagination/pagination.tsx create mode 100644 packages/ui-native/src/components/panel/panel.tsx create mode 100644 packages/ui-native/src/components/password-input/password-input.tsx create mode 100644 packages/ui-native/src/components/phone-input/phone-input.tsx create mode 100644 packages/ui-native/src/components/plan-badge/plan-badge.tsx create mode 100644 packages/ui-native/src/components/popover/popover.tsx create mode 100644 packages/ui-native/src/components/presence-stack/presence-stack.tsx create mode 100644 packages/ui-native/src/components/presence-sync-indicator/presence-sync-indicator.tsx create mode 100644 packages/ui-native/src/components/progress-bar/progress-bar.tsx create mode 100644 packages/ui-native/src/components/progress-card/progress-card.tsx create mode 100644 packages/ui-native/src/components/progress-tracker/progress-tracker.tsx create mode 100644 packages/ui-native/src/components/quiz/quiz.tsx create mode 100644 packages/ui-native/src/components/radio-group/radio-group.tsx create mode 100644 packages/ui-native/src/components/range-calendar/range-calendar.tsx create mode 100644 packages/ui-native/src/components/rating/rating.tsx create mode 100644 packages/ui-native/src/components/role-badge/role-badge.tsx create mode 100644 packages/ui-native/src/components/search-bar/search-bar.tsx create mode 100644 packages/ui-native/src/components/search-dialog/search-dialog.tsx create mode 100644 packages/ui-native/src/components/search-field/search-field.tsx create mode 100644 packages/ui-native/src/components/segmented-control/segmented-control.tsx create mode 100644 packages/ui-native/src/components/select/select.tsx create mode 100644 packages/ui-native/src/components/separator/separator.tsx create mode 100644 packages/ui-native/src/components/severity-badge/severity-badge.tsx create mode 100644 packages/ui-native/src/components/share-dialog/share-dialog.tsx create mode 100644 packages/ui-native/src/components/sheet/sheet.tsx create mode 100644 packages/ui-native/src/components/sidebar-provider/sidebar-provider.tsx create mode 100644 packages/ui-native/src/components/sidebar-toggle/sidebar-toggle.tsx create mode 100644 packages/ui-native/src/components/sidebar/sidebar.tsx create mode 100644 packages/ui-native/src/components/skeleton/skeleton.tsx create mode 100644 packages/ui-native/src/components/slider/slider.tsx create mode 100644 packages/ui-native/src/components/spinner/spinner.tsx create mode 100644 packages/ui-native/src/components/stat-card/stat-card.tsx create mode 100644 packages/ui-native/src/components/status-board/status-board.tsx create mode 100644 packages/ui-native/src/components/status-indicator/status-indicator.tsx create mode 100644 packages/ui-native/src/components/step-by-step/step-by-step.tsx create mode 100644 packages/ui-native/src/components/step-navigation/step-navigation.tsx create mode 100644 packages/ui-native/src/components/stepper/stepper.tsx create mode 100644 packages/ui-native/src/components/sticky-metric/sticky-metric.tsx create mode 100644 packages/ui-native/src/components/switch/switch.tsx create mode 100644 packages/ui-native/src/components/tabs/tabs.tsx create mode 100644 packages/ui-native/src/components/tag-group/tag-group.tsx create mode 100644 packages/ui-native/src/components/tags-input/tags-input.tsx create mode 100644 packages/ui-native/src/components/text-field/text-field.tsx create mode 100644 packages/ui-native/src/components/textarea/textarea.tsx create mode 100644 packages/ui-native/src/components/time-field/time-field.tsx create mode 100644 packages/ui-native/src/components/time-picker/time-picker.tsx create mode 100644 packages/ui-native/src/components/timeline-scrubber/timeline-scrubber.tsx create mode 100644 packages/ui-native/src/components/tldr-section/tldr-section.tsx create mode 100644 packages/ui-native/src/components/toast/toast.tsx create mode 100644 packages/ui-native/src/components/toggle-group/toggle-group.tsx create mode 100644 packages/ui-native/src/components/toggle/toggle.tsx create mode 100644 packages/ui-native/src/components/toolbar/toolbar.tsx create mode 100644 packages/ui-native/src/components/tooltip/tooltip.tsx create mode 100644 packages/ui-native/src/components/top-bar/top-bar.tsx create mode 100644 packages/ui-native/src/components/tour/tour.tsx create mode 100644 packages/ui-native/src/components/truncated-text/truncated-text.tsx create mode 100644 packages/ui-native/src/components/tutorial-complete/tutorial-complete.tsx create mode 100644 packages/ui-native/src/components/tutorial-filters/tutorial-filters.tsx create mode 100644 packages/ui-native/src/components/view-switcher/view-switcher.tsx create mode 100644 packages/ui-native/src/components/workspace-switcher/workspace-switcher.tsx create mode 100644 packages/ui-native/src/components/world-clock-bar/world-clock-bar.tsx create mode 100644 packages/ui-native/src/primitives/interaction-core-task20.test.tsx create mode 100644 packages/ui-native/src/primitives/modal-layer.tsx create mode 100644 packages/ui-native/src/primitives/platform-services.ts create mode 100644 packages/ui-native/src/primitives/selection.ts create mode 100644 packages/ui-native/src/primitives/use-controllable-state.ts create mode 100644 packages/ui-native/src/primitives/use-reduced-motion.ts create mode 100644 packages/ui/scripts/check-packed-package.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f99a2a..1dd1fa19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Release automation can regenerate this file from Conventional Commits with ### Added -- **Cross-platform foundation** — added the framework-free `@vllnt/ui-core` token/contract package, an experimental canary-only `@vllnt/ui-native` renderer with Button, Text, Heading, Badge, and Card, plus a private Expo catalog that validates Android and iOS Metro bundles. The registry, component pages, search, llms surfaces, JSON-LD, and MCP now expose web/native availability. Existing `@vllnt/ui` exports and stable publishing remain unchanged. (#479) +- **Cross-platform foundation** — added the framework-free `@vllnt/ui-core` token/contract package, an experimental source-only `@vllnt/ui-native` renderer with 171 foundation, form, data, content, AI, learning, motion, utility, control, overlay, and navigation modules, plus a private Expo catalog for Android/iOS Metro validation. The URL-driven registry UI, dedicated native hub, component pages, search, llms surfaces, JSON-LD, native manifest, and MCP expose truthful web/native availability. No native npm release exists yet; existing `@vllnt/ui` exports and stable publishing remain unchanged. (#479) - **Component family landing pages** - every component family has a standalone, SEO-oriented landing at `/families/[category]`, plus a `/families` index. One shared template renders a hero with CTAs, per-family SEO sub-groups with diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4d508b4c..dcbac219 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,6 +80,25 @@ with `git commit --no-verify`. See AGENTS.md → *React health* for details. pnpm lint && pnpm test:once && pnpm -F @vllnt/ui test:visual && pnpm build ``` +For native changes, also run: + +```bash +pnpm -F @vllnt/ui-native generate:index:check +pnpm -F @vllnt/ui-native boundaries:check +pnpm -F @vllnt/ui-native pack:check +pnpm ci:native +``` + +## Adding a native component + +1. Add `packages/ui-native/src/components/{name}/{name}.tsx` using React Native core primitives, semantic theme tokens, native accessibility APIs, controlled/uncontrolled state where applicable, and caller-owned selection IDs. +2. Do not import DOM, Radix, Tailwind, NativeWind, or browser globals. Inject capabilities such as clipboard and file selection when React Native core does not provide a portable service. +3. Add the component to `packages/ui-native/registry.json` in alphabetical order with honest `portable-options` or `native-adapted` compatibility and its native source path. +4. Run `pnpm -F @vllnt/ui-native generate:index`; never hand-maintain the generated barrel. +5. Add interaction/accessibility tests and run the native checks listed below. Update the Expo catalog when the new family needs integration proof. + +Native remains source-only until the manifest reports package availability. Do not describe the planned canary command as installable before publication. + ## Code style - TypeScript **strict** via `@vllnt/typescript`. diff --git a/README.md b/README.md index 34ba11b7..6b260a40 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ - **shadcn-compatible registry** — install individual components with `shadcn add` - **TypeScript strict** — fully typed with exported prop interfaces - **Tested** — unit tests (Vitest) + visual regression (Playwright CT) + Storybook -- **React Native pilot** — five experimental components in a separate canary-only renderer with shared tokens and contracts +- **React Native source preview** — 171 experimental native component modules in a separate renderer with shared tokens, native accessibility, and no web runtime dependency ## Install @@ -46,9 +46,9 @@ Or by `@vllnt-ui` namespace once it's in the [shadcn registry index](https://ui. pnpm dlx shadcn@latest add @vllnt-ui/button ``` -## React Native pilot +## React Native source preview -The experimental native renderer is separate so React DOM and Radix dependencies never enter Metro. Install the explicit canary channel: +The experimental native renderer is separate so React DOM and Radix dependencies never enter Metro. It currently exists in repository source only; `@vllnt/ui-native` has not been published to npm. The planned command becomes actionable only after the native manifest reports package availability: ```bash pnpm add @vllnt/ui-native@canary @@ -66,7 +66,7 @@ export function NativeExample() { } ``` -The pilot includes Button, Text, Heading, Badge, and Card. See the [React Native guide](https://ui.vllnt.com/docs/native). `@vllnt/ui` remains the stable web renderer with its existing API and release path. +The source catalog contains 171 foundation, form, data, content, AI, learning, motion, utility, control, overlay, and navigation modules. Browse the [React Native hub](https://ui.vllnt.com/native), [React Native guide](https://ui.vllnt.com/docs/native), or [machine-readable native manifest](https://ui.vllnt.com/r/native/registry.json). `@vllnt/ui` remains the stable web renderer with its existing API and release path. ## Quick Start diff --git a/ROADMAP.md b/ROADMAP.md index aa3a8e2f..a3ca8f88 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -66,24 +66,25 @@ Single-pane drill-down (chosen over accordion-single-open and a two-pane family - [ ] component-sidebar.6 Directional slide transition with `prefers-reduced-motion` instant fallback; persist last-family + scroll (localStorage) - [~] component-sidebar.7 Validate component-sidebar.1–6: Playwright E2E (desktop + mobile + keyboard) — auto-drill, back, breadcrumb sync, global filter, ⌘K, persistence (E2E); core drill-down (`.1`/`.2`/`.4`) covered by `sidebar-drilldown.spec.ts` — pending `.5`/`.6` -## native-parity [ACTIVE — experimental package pilot] +## native-parity [ACTIVE — experimental source preview] **Goal:** Add a platform-correct React Native renderer without changing the stable `@vllnt/ui` web contract. Shared tokens and semantic option contracts live in framework-free `@vllnt/ui-core`; implementations remain separate in `@vllnt/ui` and `@vllnt/ui-native`. -**Exit criteria:** the canonical token source generates unchanged web CSS and native-safe values; an Expo catalog bundles the native pilot on Android and iOS; registry JSON, docs, search, and MCP expose renderer availability; native packages can publish synchronized canaries but cannot publish `latest`. -**Verify:** existing web gates and exports stay green; `pnpm ci:native` passes contract tests, package boundaries, Expo Doctor, and both Metro exports; `/components?platform=native` and `search_components({ platform: "native" })` return the same pilot set. +**Exit criteria:** the canonical token source generates unchanged web CSS and native-safe values; an Expo catalog bundles the native source catalog on Android and iOS; registry JSON, docs, search, and MCP expose renderer availability; native packages can publish synchronized canaries but cannot publish `latest`. +**Verify:** existing web gates and exports stay green; `pnpm ci:native` passes contract tests, package boundaries, generated export/manifest drift, Expo Doctor, and both Metro exports; `/components?platform=native`, `/r/native/registry.json`, and `search_components({ platform: "native" })` return the same catalog set. The earlier co-located `.native.tsx` proposal is superseded by the package boundary tracked in #479. Separate renderers prevent DOM/Radix dependencies from entering Metro and let native APIs use `onPress`, `style`, and native accessibility semantics. The shared layer contains data and portable option names, not renderer props. Foundational native components use React Native primitives and `StyleSheet`; NativeWind and `@rn-primitives` remain possible adapters for later complex families when a demonstrated need justifies their consumer configuration and runtime cost. - [x] native-parity.1 Establish `@vllnt/ui-core` and separate `@vllnt/ui-native` package boundaries while keeping `@vllnt/ui` dependency-free from canary packages - [x] native-parity.2 Generate web CSS and native sRGB/point tokens from `packages/design/tokens.json`; fail CI on drift - [x] native-parity.3 Define portable Button, Text, Heading, Badge, and Card contracts and verify web compatibility at compile time -- [x] native-parity.4 Ship the five-component React Native pilot plus light/dark/system theme support and an Expo catalog -- [x] native-parity.5 Add explicit `platforms` and native status/parity metadata across registry JSON, docs, search, JSON-LD, llms surfaces, and MCP +- [x] native-parity.4 Ship the initial five-component React Native slice plus light/dark/system theme support and an Expo catalog +- [x] native-parity.5 Add explicit `platforms`, compatibility, source, and availability metadata across registry JSON, docs, search, JSON-LD, llms surfaces, and MCP - [x] native-parity.6 Add native quality gates and a separate synchronized canary-only workflow with no stable publish path -- [~] native-parity.7 Validate the pilot on CI and a physical Expo device; keep native experimental until both pass -- [ ] native-parity.8 Expand foundational form and utility components based on real consumer demand -- [ ] native-parity.9 Add complex primitive adapters only where native behavior and accessibility tests require them -- [ ] native-parity.10 Define stable-version policy and migration notes in a separately reviewed release change +- [~] native-parity.7 Validate the renderer on CI and physical Expo devices; keep native source-only and experimental until publication plus Android/iOS/VoiceOver/TalkBack gates pass +- [x] native-parity.8 Expand the source catalog to 171 foundation, form, data, content, AI, learning, motion, utility, control, overlay, and navigation modules +- [x] native-parity.9 Add native interaction infrastructure and adapters only where platform behavior requires them +- [ ] native-parity.10 Complete physical-device and assistive-technology validation, then enable the first synchronized canary without moving `latest` +- [ ] native-parity.11 Define stable-version policy and migration notes in a separately reviewed release change ## typography-primitives [DONE 2026-07] diff --git a/apps/native-catalog/App.test.tsx b/apps/native-catalog/App.test.tsx index 4ff16547..4d57782f 100644 --- a/apps/native-catalog/App.test.tsx +++ b/apps/native-catalog/App.test.tsx @@ -3,15 +3,23 @@ import { fireEvent, render, screen } from "@testing-library/react-native"; import App from "./App"; describe("native catalog", () => { - it("renders the pilot and proves interaction", () => { + it("renders the source catalog and proves interaction", () => { render(); expect(screen.getByText("VLLNT UI Native")).toBeOnTheScreen(); - expect(screen.getByText("Separate native renderer")).toBeOnTheScreen(); - expect(screen.getByText("Button presses: 0")).toBeOnTheScreen(); + expect(screen.getByText("Renderer boundary")).toBeOnTheScreen(); + expect(screen.getByText("Interactive composites")).toBeOnTheScreen(); + expect(screen.getByText("1 of 2 modules reviewed")).toBeOnTheScreen(); + expect(screen.getByText("Interaction count: 0")).toBeOnTheScreen(); + fireEvent.press( + screen.getByRole("checkbox", { + name: "Complete Native accessibility", + }), + ); fireEvent.press(screen.getByRole("button", { name: "Try interaction" })); - expect(screen.getByText("Button presses: 1")).toBeOnTheScreen(); + expect(screen.getByText("Catalog review complete")).toBeOnTheScreen(); + expect(screen.getByText("Interaction count: 1")).toBeOnTheScreen(); }); }); diff --git a/apps/native-catalog/App.tsx b/apps/native-catalog/App.tsx index 510a6afe..274653ab 100644 --- a/apps/native-catalog/App.tsx +++ b/apps/native-catalog/App.tsx @@ -11,11 +11,14 @@ import { StatusBar } from "expo-status-bar"; import { ScrollView, View } from "react-native"; import { - BadgeSection, - ButtonSection, CardSection, + CompositeSection, + DataSection, + FormSection, + FoundationSection, + NavigationSection, + OverlaySection, ThemeSection, - TypeSection, } from "./catalog-sections"; function CatalogContent({ @@ -27,6 +30,7 @@ function CatalogContent({ }) { const theme = useTheme(); const [presses, setPresses] = useState(0); + const [alertsEnabled, setAlertsEnabled] = useState(true); const incrementPresses = () => { setPresses((value) => value + 1); }; @@ -50,13 +54,19 @@ function CatalogContent({ VLLNT UI Native - Shared tokens and contracts. React Native renderer. + Source-only experimental renderer · 171 native component modules. - - - + + + + + + diff --git a/apps/native-catalog/README.md b/apps/native-catalog/README.md index 21460c42..49b9a013 100644 --- a/apps/native-catalog/README.md +++ b/apps/native-catalog/README.md @@ -1,10 +1,15 @@ # VLLNT UI native catalog -Private Expo consumer for the experimental `@vllnt/ui-native` package. It exercises every pilot component, semantic variant, light/dark/system theme selection, and interaction in one scrollable screen. +Private Expo integration consumer for the experimental, source-only `@vllnt/ui-native` renderer. It exercises representative foundation, form, data, feedback, navigation, overlay, AI, learning, motion, and theme surfaces from the package barrel. ```bash pnpm -F @vllnt/ui-native-catalog dev +pnpm -F @vllnt/ui-native-catalog lint +pnpm -F @vllnt/ui-native-catalog typecheck +pnpm -F @vllnt/ui-native-catalog test:once pnpm -F @vllnt/ui-native-catalog build ``` -The build exports Android and iOS JavaScript bundles in CI. This checks Metro workspace resolution without publishing or requiring a simulator. Real-device validation remains required before a stable native release. +The build exports Android and iOS JavaScript bundles in CI. This validates Expo/Metro workspace resolution without publishing or requiring a simulator. It does not replace physical Android/iOS, VoiceOver, or TalkBack validation. + +`@vllnt/ui-native` is not available from npm yet. The catalog consumes repository source and must stay aligned with `packages/ui-native/registry.json` and the generated package barrel. diff --git a/apps/native-catalog/catalog-sections.tsx b/apps/native-catalog/catalog-sections.tsx index a8942acc..b85a3052 100644 --- a/apps/native-catalog/catalog-sections.tsx +++ b/apps/native-catalog/catalog-sections.tsx @@ -1,31 +1,49 @@ import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, + AIChatInput, + Alert, + AlertDescription, + AlertTitle, + Avatar, + AvatarFallback, Badge, + Banner, Button, - type ButtonSize, - type ButtonVariant, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, + Checkbox, + Checklist, + DataList, + Dialog, + EmptyState, Heading, + Input, + NumberTicker, + ProgressBar, + SearchBar, + Separator, + Spinner, + StatusIndicator, + Switch, + Tabs, + TabsContent, + TabsList, + TabsTrigger, Text, + Textarea, type ThemeSelection, useTheme, } from "@vllnt/ui-native"; import type { ReactNode } from "react"; import { View } from "react-native"; -const buttonVariants: readonly ButtonVariant[] = [ - "default", - "secondary", - "outline", - "ghost", - "link", - "destructive", -]; -const buttonSizes: readonly ButtonSize[] = ["sm", "default", "lg", "icon"]; const themeSelections: readonly ThemeSelection[] = ["system", "light", "dark"]; function Row({ children }: { readonly children: ReactNode }) { @@ -91,7 +109,7 @@ export function ThemeSection({ ); } -export function ButtonSection({ +export function FoundationSection({ onPress, presses, }: { @@ -99,59 +117,175 @@ export function ButtonSection({ readonly presses: number; }) { return ( -
+
- {buttonVariants.map((variant) => ( - - ))} - - - {buttonSizes.map((size) => ( - - ))} + + Experimental + + + AL + + + + + Native semantic tokens - Button presses: {presses} + Interaction count: {presses} +
); } -export function TypeSection() { +export function FormSection({ + enabled, + onEnabledChange, +}: { + readonly enabled: boolean; + readonly onEnabledChange: (value: boolean) => void; +}) { return ( -
- - Semantic h3 at h1 size - - Lead body text - Default body text +
+ +